| 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``` rust
serde = { version = "1", features = ["derive"] } bson = "2.0.1"
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::initlog("requests.log", 1000, log::Level::Info, None, true);
/// 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(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!() }
* 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
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
extern crate lazy_static;
lazy_static! { static ref RB:Rbatis=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
pub async fn select(name: &str) -> BizActivity {}
//or: pub async fn select(name: &str) -> rbatis::core::Result
pub async fn testmacro() { fastlog::init_log("requests.log", 1000, log::Level::Info, None, true); 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_log("requests.log", 1000, log::Level::Info, None, true);
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
extern crate lazystatic; lazystatic! { static ref RB:Rbatis=Rbatis::new(); }
async fn index() -> impl Responder {
let v:Result
async fn main() -> std::io::Result<()> { //log fastlog::initlog("requests.log", 1000, log::Level::Info, None, true); //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 } ```
| 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 | √ |
| 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 |
English Doc * Support for DateTime and BigDecimal?
Currently supports chrono::NaiveDateTime和bigdecimal::BigDecimal * Supports forasync/.await
Currently supports bothasync_std
andtokio
* Stmt in postgres uses $1, $2 instead of ? in Mysql, does this require some special treatment? No, because rbatis uses #{} to describe parametric variabls, you only need to write the correct parameter names and do not need to match it with the symbols used by the database. * Supports for Oracle database driver?
No, moving away from IOE is recommended. * Which crate should be depended on if only the driver is needed?
rbatis-core, Cargo.toml add rbatis-core = "*" * How to selectasync/.await
runtime?
see https://rbatis.github.io/rbatis.io/#/en/ * column "id" is of type uuid but expression is of type text'?
see https://rbatis.github.io/rbatis.io/#/en/?id=database-column-formatting-macro * How to use '::uuid','::timestamp' on PostgreSQL?
see https://rbatis.github.io/rbatis.io/#/en/?id=database-column-formatting-macro
捐赠
联系方式(添加好友请备注'rbatis') 微信群:先加微信,然后拉进群