use mybatis_core::db::DBConnectOption;
use once_cell::sync::OnceCell;
use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use std::borrow::BorrowMut;
use std::cell::Cell;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use uuid::Uuid;
use crate::executor::{MyBatisConnExecutor, MyBatisExecutor, MyBatisTxExecutor};
use crate::intercept::SqlIntercept;
use crate::log::{LogPlugin, MyBatisLogPlugin};
use crate::logic_delete::{LogicDelete, MyBatisLogicDeletePlugin};
use crate::page::{IPage, IPageRequest, MyBatisPagePlugin, Page, PagePlugin};
use crate::plus::MybatisPlus;
use crate::snowflake::new_snowflake_id;
use crate::wrapper::Wrapper;
use mybatis_core::db::{
DBExecResult, DBPool, DBPoolConn, DBPoolOptions, DBQuery, DBTx, DriverType,
};
use mybatis_core::Error;
use mybatis_sql::PageLimit;
use mybatis_util::error_util::ToResult;
use std::fmt::{Debug, Formatter};
pub struct Mybatis {
pub pool: OnceCell<DBPool>,
pub page_plugin: Box<dyn PagePlugin>,
pub sql_intercepts: Vec<Box<dyn SqlIntercept>>,
pub log_plugin: Arc<Box<dyn LogPlugin>>,
pub logic_plugin: Option<Box<dyn LogicDelete>>,
pub encoder: fn(q: &mut DBQuery, arg: rbson::Bson) -> crate::Result<()>,
}
impl Debug for Mybatis {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Mybatis")
.field("pool", &self.pool)
.field("page_plugin", &self.page_plugin)
.field("sql_intercepts", &self.sql_intercepts)
.field("logic_plugin", &self.logic_plugin)
.finish()
}
}
impl Default for Mybatis {
fn default() -> Mybatis {
Mybatis::new()
}
}
#[derive(Debug)]
pub struct MybatisOption {
pub page_plugin: Box<dyn PagePlugin>,
pub sql_intercepts: Vec<Box<dyn SqlIntercept>>,
pub log_plugin: Arc<Box<dyn LogPlugin>>,
pub logic_plugin: Option<Box<dyn LogicDelete>>,
}
impl Default for MybatisOption {
fn default() -> Self {
Self {
page_plugin: Box::new(MyBatisPagePlugin::new()),
sql_intercepts: vec![],
logic_plugin: None,
log_plugin: Arc::new(Box::new(MyBatisLogPlugin::default()) as Box<dyn LogPlugin>),
}
}
}
impl Mybatis {
pub fn new() -> Self {
return Self::new_with_opt(MybatisOption::default());
}
pub fn new_with_opt(option: MybatisOption) -> Self {
return Self {
pool: OnceCell::new(),
page_plugin: option.page_plugin,
sql_intercepts: option.sql_intercepts,
logic_plugin: option.logic_plugin,
log_plugin: option.log_plugin,
encoder: |q, arg| {
q.bind_value(arg)?;
Ok(())
},
};
}
pub fn new_wrapper(&self) -> Wrapper {
let driver = self.driver_type();
if driver.as_ref().unwrap().eq(&DriverType::None) {
panic!("[mybatis] .new_wrapper() method must be call .link(url) to init first!");
}
Wrapper::new(&driver.unwrap_or_else(|_| {
panic!("[mybatis] .new_wrapper() method must be call .link(url) to init first!");
}))
}
pub fn new_wrapper_table<T>(&self) -> Wrapper
where
T: MybatisPlus,
{
let mut w = self.new_wrapper();
let formats = T::formats(self.driver_type().unwrap());
w = w.set_formats(formats);
return w;
}
pub async fn link(&self, driver_url: &str) -> Result<(), Error> {
return Ok(self.link_opt(driver_url, DBPoolOptions::default()).await?);
}
pub async fn link_opt(
&self,
driver_url: &str,
pool_options: DBPoolOptions,
) -> Result<(), Error> {
if driver_url.is_empty() {
return Err(Error::from("[mybatis] link url is empty!"));
}
let pool = DBPool::new_opt_str(driver_url, pool_options).await?;
self.pool.set(pool);
return Ok(());
}
pub async fn link_cfg(
&self,
connect_option: &DBConnectOption,
pool_options: DBPoolOptions,
) -> Result<(), Error> {
let pool = DBPool::new_opt(connect_option, pool_options).await?;
self.pool.set(pool);
return Ok(());
}
pub fn set_log_plugin(&mut self, arg: impl LogPlugin + 'static) {
self.log_plugin = Arc::new(Box::new(arg));
}
pub fn set_logic_plugin(&mut self, arg: impl LogicDelete + 'static) {
self.logic_plugin = Some(Box::new(arg));
}
pub fn set_page_plugin(&mut self, arg: impl PagePlugin + 'static) {
self.page_plugin = Box::new(arg);
}
pub fn add_sql_intercept(&mut self, arg: impl SqlIntercept + 'static) {
self.sql_intercepts.push(Box::new(arg));
}
pub fn set_sql_intercepts(&mut self, arg: Vec<Box<dyn SqlIntercept>>) {
self.sql_intercepts = arg;
}
pub fn get_pool(&self) -> Result<&DBPool, Error> {
let p = self.pool.get();
if p.is_none() {
return Err(Error::from("[mybatis] mybatis pool not inited!"));
}
return Ok(p.unwrap());
}
pub fn driver_type(&self) -> Result<DriverType, Error> {
let pool = self.get_pool()?;
Ok(pool.driver_type())
}
pub async fn acquire(&self) -> Result<MyBatisConnExecutor<'_>, Error> {
let pool = self.get_pool()?;
let conn = pool.acquire().await?;
return Ok(MyBatisConnExecutor {
conn: conn,
rb: &self,
});
}
pub async fn acquire_begin(&self) -> Result<MyBatisTxExecutor<'_>, Error> {
let pool = self.get_pool()?;
let conn = pool.begin().await?;
return Ok(MyBatisTxExecutor {
tx_id: new_snowflake_id(),
conn: conn,
rb: &self,
});
}
pub fn is_debug_mode(&self) -> bool {
if cfg!(feature = "debug_mode") {
return true;
}
return false;
}
pub fn as_executor(&self) -> MyBatisExecutor {
self.into()
}
}
pub trait AsSqlTag {
fn sql_tag(&self) -> char;
fn do_replace_tag(&self, sql: &mut String);
}
impl AsSqlTag for DriverType {
#[inline]
fn sql_tag(&self) -> char {
match self {
DriverType::None => '?',
DriverType::Mysql => '?',
DriverType::Sqlite => '?',
DriverType::Postgres => '$',
DriverType::Mssql => '$',
}
}
#[inline]
fn do_replace_tag(&self, sql: &mut String) {
if self.eq(&DriverType::Mssql) {
*sql = sql.replace("$", "@p");
}
}
}