# add this library,and cargo install
# bson(required)
serde = { version = "1", features = ["derive"]}
rbson = "2.0"
# logging lib(required)
log = "0.4"
fast_log="1.3"
# rbatis (required) default is all-database+runtime-async-std-rustls
rbatis = { version = "3.0"}
# also if you use actix-web+mysql
# rbatis = { version = "3.0",default-features = false, features = ["mysql","runtime-async-std-rustls"]}
Quick example: QueryWrapper and common usages (see example/crud_test.rs for details)
//#[macro_use] define in 'root crate' or 'mod.rs' or 'main.rs'#[macro_use]externcrate rbatis;use rbatis::crud::CRUD;/// may also write `CRUDTable` as `impl CRUDTable for BizActivity{}`/// #[crud_table]/// #[crud_table(table_name:biz_activity)]/// #[crud_table(table_name:"biz_activity"|table_columns:"id,name,version,delete_flag")]/// #[crud_table(table_name:"biz_activity"|table_columns:"id,name,version,delete_flag"|formats_pg:"id:{}::uuid")]#[crud_table]#[derive(Clone,Debug)]pubstructBizActivity{pubid:Option<String>,pubname:Option<String>,pubpc_link:Option<String>,pubh5_link:Option<String>,pubpc_banner_img:Option<String>,pubh5_banner_img:Option<String>,pubsort:Option<String>,pubstatus:Option<i32>,pubremark:Option<String>,pubcreate_time:Option<rbatis::DateTimeNative>,pubversion:Option<i32>,pubdelete_flag:Option<i32>,}// this macro will create impl BizActivity{ pub fn id()->&str ..... }impl_field_name_method!(BizActivity{id,name});/// (optional) manually implement instead of using `derive(CRUDTable)`. This allows manually rewriting `table_name()` function and supports code completion in IDE./// (option) but this struct require #[derive(Serialize,Deserialize)]// use rbatis::crud::CRUDTable;//impl CRUDTable for BizActivity { // fn table_name()->String{// "biz_activity".to_string()// }// fn table_columns()->String{// "id,name,delete_flag".to_string()// }//}#[tokio::main]asyncfnmain(){/// enable log crate to show sql logs
fast_log::init_log("requests.log", log::Level::Info,None,true);/// initialize rbatis. May use `lazy_static` crate to define rbatis as a global variable because rbatis is thread safelet rb = Rbatis::new();/// connect to database
rb.link("mysql://root:123456@localhost:3306/test").await.unwrap();/// customize connection pool parameters (optional)// let mut opt =PoolOptions::new();// opt.max_size=100;// rb.link_opt("mysql://root:123456@localhost:3306/test",&opt).await.unwrap();/// newly constructed wrapper sql logiclet wrapper = rb.new_wrapper().eq("id",1)//sql: id = 1.and()//sql: and .ne(BizActivity::id(),1)//sql: id <> 1.in_array("id",&[1,2,3])//sql: id in (1,2,3).not_in("id",&[1,2,3])//sql: id not in (1,2,3).like("name",1)//sql: name like 1.or()//sql: or.not_like(BizActivity::name(),"asdf")//sql: name not like 'asdf'.between("create_time","2020-01-01 00:00:00","2020-12-12 00:00:00")//sql: create_time between '2020-01-01 00:00:00' and '2020-01-01 00:00:00'.group_by(&["id"])//sql: group by id.order_by(true,&["id","name"])//sql: group by id,name;let activity = BizActivity{id:Some("12312".to_string()),name:None,pc_link:None,h5_link:None,pc_banner_img:None,h5_banner_img:None,sort:None,status:None,remark:None,create_time:Some(rbatis::DateTimeNative::now()),version:Some(1),delete_flag:Some(1),};/// saving
rb.save(&activity,&[]).await;//Exec ==> INSERT INTO biz_activity (create_time,delete_flag,h5_banner_img,h5_link,id,name,pc_banner_img,pc_link,remark,sort,status,version) VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )/// batch saving
rb.save_batch(&vec![activity],&[]).await;//Exec ==> INSERT INTO biz_activity (create_time,delete_flag,h5_banner_img,h5_link,id,name,pc_banner_img,pc_link,remark,sort,status,version) VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? ),( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )/// fetch allow None or one result. column you can use BizActivity::id() or "id"let result:Option<BizActivity> = rb.fetch_by_column(BizActivity::id(),"1").await.unwrap();//Query ==> SELECT create_time,delete_flag,h5_banner_img,h5_link,id,name,pc_banner_img,pc_link,remark,sort,status,version FROM biz_activity WHERE delete_flag = 1 AND id = ? /// query alllet result:Vec<BizActivity> = rb.list().await.unwrap();//Query ==> SELECT create_time,delete_flag,h5_banner_img,h5_link,id,name,pc_banner_img,pc_link,remark,sort,status,version FROM biz_activity WHERE delete_flag = 1///query by id veclet result:Vec<BizActivity> = rb.list_by_column("id",&["1"]).await.unwrap();//Query ==> SELECT create_time,delete_flag,h5_banner_img,h5_link,id,name,pc_banner_img,pc_link,remark,sort,status,version FROM biz_activity WHERE delete_flag = 1 AND id IN (?) ///query by wrapperlet r:Result<Option<BizActivity>,Error> = rb.fetch_by_wrapper(rb.new_wrapper().eq("id","1")).await;//Query ==> SELECT create_time,delete_flag,h5_banner_img,h5_link,id,name,pc_banner_img,pc_link,remark,sort,status,version FROM biz_activity WHERE delete_flag = 1 AND id = ? ///delete
rb.remove_by_column::<BizActivity,_>("id",&"1").await;//Exec ==> UPDATE biz_activity SET delete_flag = 0 WHERE id = 1///delete batch
rb.remove_batch_by_column::<BizActivity,_>("id",&["1","2"]).await;//Exec ==> UPDATE biz_activity SET delete_flag = 0 WHERE id IN ( ? , ? ) ///updateletmut activity = activity.clone();let r = rb.update_by_column("id",&activity).await;//Exec ==> update biz_activity set status = ?, create_time = ?, version = ?, delete_flag = ? where id = ?
rb.update_by_wrapper(&activity, rb.new_wrapper().eq("id","12312"),&[Skip::Value(&serde_json::Value::Null),Skip::Column("id")]).await;//Exec ==> UPDATE biz_activity SET create_time = ? , delete_flag = ? , status = ? , version = ? WHERE id = ? }///...more usage,see crud.rs
macros (new addition)
Important update (pysql removes runtime, directly compiles to static rust code) This means that the performance of
SQL generated using py_sql,html_sql is roughly similar to that of handwritten code.
Because of the compile time, the annotations need to declare the database type to be used
#[py_sql( rb,"select * from biz_activity where delete_flag = 0 if name != '': and name=#{name}")]asyncfnpy_sql_tx(rb:&Rbatis,tx_id:&String,name:&str) -> Vec<BizActivity>{todo!()}
Added html_sql support, a form of organization similar to MyBatis, to facilitate migration of Java systems to Rust(
Note that it is also compiled as Rust code at build time and performs close to handwritten code) this is very faster
Because of the compile time, the annotations need to declare the database type to be used
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "https://github.com/rbatis/rbatis_sql/raw/main/mybatis-3-mapper.dtd"><mapper><selectid="select_by_condition">
select * from biz_activity where
<iftest="name != ''">
name like #{name}
</if></select></mapper>
///select page must have '?:&PageRequest' arg and return 'Page<?>'#[html_sql(rb, "example/example.html")]asyncfnselect_by_condition(rb:&mutRbatisExecutor<'_,'_>,page_req:&PageRequest,name:&str) -> Page<BizActivity>{todo!()}
use once_cell::sync::Lazy;pubstaticRB:Lazy<Rbatis> = Lazy::new(||Rbatis::new());/// Macro generates execution logic based on method definition, similar to @select dynamic SQL of Java/Mybatis/// RB is the name referenced locally by Rbatis, for example DAO ::RB, com:: XXX ::RB... Can be/// The second parameter is the standard driver SQL. Note that the corresponding database parameter mysql is? , pg is $1.../// macro auto edit method to 'pub async fn select(name: &str) -> rbatis::core::Result<BizActivity> {}'///#[sql(RB, "select * from biz_activity where id = ?")]pubasyncfnselect(name:&str) -> BizActivity{}//or: pub async fn select(name: &str) -> rbatis::core::Result<BizActivity> {}#[tokio::test]pubasyncfntest_macro(){
fast_log::init_log("requests.log", log::Level::Info,None,true);RB.link("mysql://root:123456@localhost:3306/test").await.unwrap();let a = select("1").await.unwrap();println!("{:?}", a);}
How to use logical deletes plugin (works for fetching or removing functions provided by rbatis,e.g. list**(),remove**(),fetch**())
let rb = Rbatis::new();
rb.link("mysql://root:123456@localhost:3306/test").await.unwrap();letmut tx = rb.acquire_begin().await.unwrap();let v: serde_json::Value = tx
.fetch("select count(1) from biz_activity;",&vec![]).await.unwrap();println!("{}", v.clone());//rb.fetch**** and more method
tx.commit().await.unwrap();//tx.begin().await
Transaction defer
pubasyncfnforget_commit(rb:&Rbatis) -> rbatis::core::Result<()>{// tx will be commit.when func endletmut tx = rb.acquire_begin().await?.defer_async(|mut tx1| asyncmove{if !tx1.is_done(){
tx1.rollback().await;println!("tx rollback success!");}else{println!("don't need rollback!");}});let v = tx
.exec("update biz_activity set name = '6' where id = 1;",&vec![]).await;//tx.commit().await; //if commit, print 'don't need rollback!' ,if not,print 'tx rollback success!'returnOk(());}
How to use rbatis with Rust web frameworks (actix-web is used here as an example, but all web frameworks based on tokio or async_std are supported)
CRUD, with built-in CRUD template (built-in CRUD supports logical deletes)
√
LogSystem (logging component)
√
Tx(task/Nested transactions)
√
Py(using py-like statement in SQL)
√
async/await support
√
PagePlugin(Pagincation)
√
LogicDelPlugin
√
Html(xml) Compile time dynamic SQL)
√
DataBase Table ConvertPage(Web UI,Coming soon)
x
Conlusion: Assuming zero time consumed on IO, single threaded benchmark achieves 200K QPS or QPS, which is a few times
more performant than GC languages like Go or Java.