1use mybatis_core::db::DBConnectOption;
2use once_cell::sync::OnceCell;
3use serde::de::DeserializeOwned;
4use serde::ser::Serialize;
5use std::borrow::BorrowMut;
6use std::cell::Cell;
7use std::collections::HashMap;
8use std::sync::Arc;
9use std::time::Duration;
10use uuid::Uuid;
11
12use crate::executor::{MyBatisConnExecutor, MyBatisExecutor, MyBatisTxExecutor};
13use crate::intercept::SqlIntercept;
14use crate::log::{LogPlugin, MyBatisLogPlugin};
15use crate::logic_delete::{LogicDelete, MyBatisLogicDeletePlugin};
16use crate::page::{IPage, IPageRequest, MyBatisPagePlugin, Page, PagePlugin};
17use crate::plus::MybatisPlus;
18use crate::snowflake::new_snowflake_id;
19use crate::wrapper::Wrapper;
20use mybatis_core::db::{
21 DBExecResult, DBPool, DBPoolConn, DBPoolOptions, DBQuery, DBTx, DriverType,
22};
23use mybatis_core::Error;
24use mybatis_sql::PageLimit;
25use mybatis_util::error_util::ToResult;
26use std::fmt::{Debug, Formatter};
27
28pub struct Mybatis {
31 pub pool: OnceCell<DBPool>,
33 pub page_plugin: Box<dyn PagePlugin>,
35 pub sql_intercepts: Vec<Box<dyn SqlIntercept>>,
37 pub log_plugin: Arc<Box<dyn LogPlugin>>,
39 pub logic_plugin: Option<Box<dyn LogicDelete>>,
41 pub encoder: fn(q: &mut DBQuery, arg: rbson::Bson) -> crate::Result<()>,
43}
44
45impl Debug for Mybatis {
46 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
47 f.debug_struct("Mybatis")
48 .field("pool", &self.pool)
49 .field("page_plugin", &self.page_plugin)
50 .field("sql_intercepts", &self.sql_intercepts)
51 .field("logic_plugin", &self.logic_plugin)
52 .finish()
53 }
54}
55
56impl Default for Mybatis {
57 fn default() -> Mybatis {
58 Mybatis::new()
59 }
60}
61
62#[derive(Debug)]
64pub struct MybatisOption {
65 pub page_plugin: Box<dyn PagePlugin>,
67 pub sql_intercepts: Vec<Box<dyn SqlIntercept>>,
69 pub log_plugin: Arc<Box<dyn LogPlugin>>,
71 pub logic_plugin: Option<Box<dyn LogicDelete>>,
73}
74
75impl Default for MybatisOption {
76 fn default() -> Self {
77 Self {
78 page_plugin: Box::new(MyBatisPagePlugin::new()),
79 sql_intercepts: vec![],
80 logic_plugin: None,
81 log_plugin: Arc::new(Box::new(MyBatisLogPlugin::default()) as Box<dyn LogPlugin>),
82 }
83 }
84}
85
86impl Mybatis {
87 pub fn new() -> Self {
89 return Self::new_with_opt(MybatisOption::default());
90 }
91
92 pub fn new_with_opt(option: MybatisOption) -> Self {
94 return Self {
95 pool: OnceCell::new(),
96 page_plugin: option.page_plugin,
97 sql_intercepts: option.sql_intercepts,
98 logic_plugin: option.logic_plugin,
99 log_plugin: option.log_plugin,
100 encoder: |q, arg| {
101 q.bind_value(arg)?;
102 Ok(())
103 },
104 };
105 }
106
107 pub fn new_wrapper(&self) -> Wrapper {
109 let driver = self.driver_type();
110 if driver.as_ref().unwrap().eq(&DriverType::None) {
111 panic!("[mybatis] .new_wrapper() method must be call .link(url) to init first!");
112 }
113 Wrapper::new(&driver.unwrap_or_else(|_| {
114 panic!("[mybatis] .new_wrapper() method must be call .link(url) to init first!");
115 }))
116 }
117
118 pub fn new_wrapper_table<T>(&self) -> Wrapper
120 where
121 T: MybatisPlus,
122 {
123 let mut w = self.new_wrapper();
124 let formats = T::formats(self.driver_type().unwrap());
125 w = w.set_formats(formats);
126 return w;
127 }
128
129 pub async fn link(&self, driver_url: &str) -> Result<(), Error> {
131 return Ok(self.link_opt(driver_url, DBPoolOptions::default()).await?);
132 }
133
134 pub async fn link_opt(
140 &self,
141 driver_url: &str,
142 pool_options: DBPoolOptions,
143 ) -> Result<(), Error> {
144 if driver_url.is_empty() {
145 return Err(Error::from("[mybatis] link url is empty!"));
146 }
147 let pool = DBPool::new_opt_str(driver_url, pool_options).await?;
148 self.pool.set(pool);
149 return Ok(());
150 }
151
152 pub async fn link_cfg(
157 &self,
158 connect_option: &DBConnectOption,
159 pool_options: DBPoolOptions,
160 ) -> Result<(), Error> {
161 let pool = DBPool::new_opt(connect_option, pool_options).await?;
162 self.pool.set(pool);
163 return Ok(());
164 }
165
166 pub fn set_log_plugin(&mut self, arg: impl LogPlugin + 'static) {
167 self.log_plugin = Arc::new(Box::new(arg));
168 }
169
170 pub fn set_logic_plugin(&mut self, arg: impl LogicDelete + 'static) {
171 self.logic_plugin = Some(Box::new(arg));
172 }
173
174 pub fn set_page_plugin(&mut self, arg: impl PagePlugin + 'static) {
175 self.page_plugin = Box::new(arg);
176 }
177
178 pub fn add_sql_intercept(&mut self, arg: impl SqlIntercept + 'static) {
179 self.sql_intercepts.push(Box::new(arg));
180 }
181
182 pub fn set_sql_intercepts(&mut self, arg: Vec<Box<dyn SqlIntercept>>) {
183 self.sql_intercepts = arg;
184 }
185
186 pub fn get_pool(&self) -> Result<&DBPool, Error> {
188 let p = self.pool.get();
189 if p.is_none() {
190 return Err(Error::from("[mybatis] mybatis pool not inited!"));
191 }
192 return Ok(p.unwrap());
193 }
194
195 pub fn driver_type(&self) -> Result<DriverType, Error> {
197 let pool = self.get_pool()?;
198 Ok(pool.driver_type())
199 }
200
201 pub async fn acquire(&self) -> Result<MyBatisConnExecutor<'_>, Error> {
203 let pool = self.get_pool()?;
204 let conn = pool.acquire().await?;
205 return Ok(MyBatisConnExecutor {
206 conn: conn,
207 rb: &self,
208 });
209 }
210
211 pub async fn acquire_begin(&self) -> Result<MyBatisTxExecutor<'_>, Error> {
213 let pool = self.get_pool()?;
214 let conn = pool.begin().await?;
215 return Ok(MyBatisTxExecutor {
216 tx_id: new_snowflake_id(),
217 conn: conn,
218 rb: &self,
219 });
220 }
221
222 pub fn is_debug_mode(&self) -> bool {
224 if cfg!(feature = "debug_mode") {
225 return true;
226 }
227 return false;
228 }
229
230 pub fn as_executor(&self) -> MyBatisExecutor {
232 self.into()
233 }
234}
235
236pub trait AsSqlTag {
237 fn sql_tag(&self) -> char;
238 fn do_replace_tag(&self, sql: &mut String);
239}
240
241impl AsSqlTag for DriverType {
242 #[inline]
243 fn sql_tag(&self) -> char {
244 match self {
245 DriverType::None => '?',
246 DriverType::Mysql => '?',
247 DriverType::Sqlite => '?',
248 DriverType::Postgres => '$',
249 DriverType::Mssql => '$',
251 }
252 }
253 #[inline]
254 fn do_replace_tag(&self, sql: &mut String) {
255 if self.eq(&DriverType::Mssql) {
256 *sql = sql.replace("$", "@p");
257 }
258 }
259}