| Framework | Async/.await | Learning curve | Dynamic SQL/py/Wrapper/built-in CRUD | Logical delete plugin| Pagination plugin
| ------ | ------ |------ |------ |------ |------ |
| rbatis | √ | easy | √ | √ | √ |
| sqlx | √ | hard (depends on macros and env. variables) | x | x | x |
| diesel | x | hard (use FFI, unsafe) | x | x | x |
| Framework | Mysql(docker) | SQL statement(10k) | ns/operation(lower is better) | Qps(higher is better) |Memory usage(lower is better) |
| ------ | ------ |------ |------ |------ |------ |
| Rust-rbatis/tokio | 1 CPU, 1G memory | select count(1) from table; | 965649 ns/op | 1035 Qps/s | 2.1MB |
| Go-GoMybatis/http | 1 CPU, 1G memory | select count(1) from table; | 1184503 ns/op | 844 Qps/s | 28.4MB |
log
crate#![forbid(unsafe_code)]
enabled| data structure | is supported |
| ------ | ------ |
| Option | √ |
| Vec | √ |
| HashMap | √ |
| i32,i64,f32,f64,bool,String...more rust type | √ |
| rbatis::Bytes | √ |
| rbatis::DateNative | √ |
| rbatis::DateUtc | √ |
| rbatis::DateTimeNative | √ |
| rbatis::DateTimeUtc | √ |
| rbatis::Decimal | √ |
| rbatis::Json
| rbatis::TimeNative | √ |
| rbatis::TimeUtc | √ |
| rbatis::Timestamp | √ |
| rbatis::TimestampZ | √ |
| rbatis::Uuid | √ |
| rbatis::plugin::page::{Page
| database | is supported |
| ------ | ------ |
| Mysql | √ |
| Postgres | √ |
| Sqlite | √ |
| Mssql/Sqlserver | √(50%) |
| MariaDB(Mysql) | √ |
| TiDB(Mysql) | √ |
| CockroachDB(Postgres) | √ |
| platform | is supported |
| ------ | ------ |
| Linux | √ |
| Apple/MacOS | √ |
| Windows | √ |
``` rust
serde = { version = "1", features = ["derive"] } rbson = "2.0"
log = "0.4" fast_log="1.3"
rbatis = { version = "3.0" }
```
```rust //#[macro_use] define in 'root crate' or 'mod.rs' or 'main.rs'
extern crate rbatis;
use rbatis::crud::CRUD;
/// may also write CRUDTable
as impl CRUDTable for BizActivity{}
/// #[crudtable]
/// #[crudtable(tablename:bizactivity)]
/// #[crudtable(tablename:"bizactivity"|tablecolumns:"id,name,version,deleteflag")]
/// #[crudtable(tablename:"bizactivity"|tablecolumns:"id,name,version,deleteflag"|formats_pg:"id:{}::uuid")]
pub struct BizActivity {
pub id: Option
// this macro will create impl BizActivity{ pub fn id()->&str ..... } implfieldname_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 tablename()->String{
// "bizactivity".tostring()
// }
// fn tablecolumns()->String{
// "id,name,deleteflag".tostring()
// }
//}
async fn main() {
/// enable log crate to show sql logs
fastlog::init(fastlog::config::Config::new().console());
/// initialize rbatis. May use lazy_static
crate to define rbatis as a global variable because rbatis is thread safe
let 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.maxsize=100;
// rb.linkopt("mysql://root:123456@localhost:3306/test",&opt).await.unwrap();
/// newly constructed wrapper sql logic
let wrapper = rb.newwrapper()
.eq("id", 1) //sql: id = 1
.and() //sql: and
.ne(BizActivity::id(), 1) //sql: id <> 1
.inarray("id", &[1, 2, 3]) //sql: id in (1,2,3)
.notin("id", &[1, 2, 3]) //sql: id not in (1,2,3)
.like("name", 1) //sql: name like 1
.or() //sql: or
.notlike(BizActivity::name(), "asdf") //sql: name not like 'asdf'
.between("createtime", "2020-01-01 00:00:00", "2020-12-12 00:00:00")//sql: createtime between '2020-01-01 00:00:00' and '2020-01-01 00:00:00'
.groupby(&["id"]) //sql: group by id
.orderby(true, &["id", "name"])//sql: group by id,name
;
let activity = BizActivity { id: Some("12312".tostring()), name: None, pclink: None, h5link: None, pcbannerimg: None, h5bannerimg: None, sort: None, status: None, remark: None, createtime: Some(rbatis::DateTimeNative::now()), version: Some(1), deleteflag: Some(1), }; /// saving rb.save(&activity, &[]).await; //Exec ==> INSERT INTO bizactivity (createtime,deleteflag,h5bannerimg,h5link,id,name,pcbannerimg,pclink,remark,sort,status,version) VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )
/// batch saving rb.savebatch(&vec![activity], &[]).await; //Exec ==> INSERT INTO bizactivity (createtime,deleteflag,h5bannerimg,h5link,id,name,pcbannerimg,pclink,remark,sort,status,version) VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? ),( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )
/// fetch allow None or one result. column you can use BizActivity::id() or "id"
let result: Option
/// query all
let result: Vec
///query by id vec
let result: Vec
///query by wrapper let r: Result
///delete
rb.removebycolumn::
///delete batch
rb.removebatchbycolumn::
///update let mut activity = activity.clone(); let r = rb.updatebycolumn("id", &activity).await; //Exec ==> update bizactivity set status = ?, createtime = ?, version = ?, deleteflag = ? where id = ? rb.updatebywrapper(&activity, rb.newwrapper().eq("id", "12312"), &[Skip::Value(&serdejson::Value::Null), Skip::Column("id")]).await; //Exec ==> UPDATE bizactivity SET createtime = ? , deleteflag = ? , status = ? , version = ? WHERE id = ? }
///...more usage,see crud.rs ```
Because of the compile time, the annotations need to declare the database type to be used
rust
#[py_sql(
rb,
"select * from biz_activity where delete_flag = 0
if name != '':
and name=#{name}")]
async fn py_sql_tx(rb: &Rbatis, tx_id: &String, name: &str) -> Vec<BizActivity> { todo!() }
Because of the compile time, the annotations need to declare the database type to be used
html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "https://github.com/rbatis/rbatis_sql/raw/main/mybatis-3-mapper.dtd">
<mapper>
<select id="select_by_condition">
select * from biz_activity where
<if test="name != ''">
name like #{name}
</if>
</select>
</mapper>
rust
///select page must have '?:&PageRequest' arg and return 'Page<?>'
#[html_sql(rb, "example/example.html")]
async fn select_by_condition(rb: &mut RbatisExecutor<'_,'_>, page_req: &PageRequest, name: &str) -> Page<BizActivity> { todo!() }
```rust
use once_cell::sync::Lazy;
pub static RB:Lazy
/// 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
pub async fn select(name: &str) -> BizActivity {}
//or: pub async fn select(name: &str) -> rbatis::core::Result
pub async fn testmacro() { fastlog::init(fast_log::config::Config::new().console()); RB.link("mysql://root:123456@localhost:3306/test").await.unwrap(); let a = select("1").await.unwrap(); println!("{:?}", a); } ```
rust
let mut rb:Rbatis=Rbatis::new();
//rb.logic_plugin = Some(Box::new(RbatisLogicDeletePlugin::new_opt("delete_flag",1,0)));//Customize deleted/undeleted writing
rb.set_logic_plugin(RbatisLogicDeletePlugin::<BizActivity>::new("delete_flag"));;
rb.link("mysql://root:123456@localhost:3306/test").await.unwrap();
let r = rb.remove_batch_by_id::<BizActivity>( & ["1", "2"]).await;
if r.is_err() {
println ! ("{}", r.err().unwrap().to_string());
}
```rust let mut rb = Rbatis::new(); rb.link("mysql://root:123456@localhost:3306/test").await.unwrap(); //replace page plugin //rb.page_plugin = Box::new(RbatisPagePlugin::new());
let req = PageRequest::new(1, 20);
let wraper= rb.newwrapper()
.eq("deleteflag", 1);
let data: Page
//2020-07-10T21:28:40.036506700+08:00 INFO rbatis::rbatis - [rbatis] Query ==> SELECT count(1) FROM bizactivity WHERE deleteflag = ? LIMIT 0,20 //2020-07-10T21:28:40.040505200+08:00 INFO rbatis::rbatis - [rbatis] Args ==> [1] //2020-07-10T21:28:40.073506+08:00 INFO rbatis::rbatis - [rbatis] Total <== 1 //2020-07-10T21:28:40.073506+08:00 INFO rbatis::rbatis - [rbatis] Query ==> SELECT createtime,deleteflag,h5bannerimg,h5link,id,name,pcbannerimg,pclink,remark,sort,status,version FROM bizactivity WHERE deleteflag = ? LIMIT 0,20 //2020-07-10T21:28:40.073506+08:00 INFO rbatis::rbatis - [rbatis] Args ==> [1] //2020-07-10T21:28:40.076506500+08:00 INFO rbatis::rbatis - [rbatis] Total <== 5 ```
json
{
"records": [
{
"id": "12312",
"name": "null",
"pc_link": "null",
"h5_link": "null",
"pc_banner_img": "null",
"h5_banner_img": "null",
"sort": "null",
"status": 1,
"remark": "null",
"create_time": "2020-02-09T00:00:00+00:00",
"version": 1,
"delete_flag": 1
}
],
"total": 5,
"size": 20,
"current": 1,
"serch_count": true
}
rust
use log::{error, info, warn};
fn main(){
fast_log::init(fast_log::config::Config::new().console());
info!("print data");
}
```rust use rbatis::core::db::PoolOptions;
pub async fn initrbatis() -> Rbatis { let rb = Rbatis::new(); let mut opt = PoolOptions::new(); opt.maxsize = 20; rb.link_opt("mysql://root:123456@localhost:3306/test", &opt).await.unwrap(); } ```
rust
let rb = Rbatis::new();
rb.link("mysql://root:123456@localhost:3306/test").await.unwrap();
let mut 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
rust
pub async fn forget_commit(rb: &Rbatis) -> rbatis::core::Result<()> {
// tx will be commit.when func end
let mut tx = rb.acquire_begin().await?.defer_async(|mut tx1| async move {
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!'
return Ok(());
}
``` rust
use once_cell::sync::Lazy;
pub static RB:Lazy
async fn index() -> impl Responder {
let v:Result
async fn main() -> std::io::Result<()> { //log fastlog::init(fastlog::config::Config::new().console()); //link database RB.link("mysql://root:123456@localhost:3306/test").await.unwrap(); //http server HttpServer::new(|| { App::new() .route("/", web::get().to(index)) }) .bind("127.0.0.1:8000")? .run() .await } ```
| function | is 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 |
async/.await
async_std
and tokio
async/.await
runtime? 捐赠
联系方式(添加好友请备注'rbatis') 微信群:先加微信,然后拉进群