A ORM formwork Rustlang-based,dynamic sql, no Runtime,No Garbage Collector, low Memory use,High Performance orm Framework. support async_std,tokio

This crate uses #![forbid(unsafe_code)] to ensure everything is implemented in 100% Safe Rust.

rbatis 是一个无GC无虚拟机无运行时Runtime直接编译为机器码,并发安全的 数据库 ORM框架,并且所有数据传值均使用json(serde_json)使用百分之百的安全代码实现

Build Status

Image text

way not diesel,way not sqlx ? 为什么不选择diesel,sqlx之类的框架?

| 框架 | 协程异步async高并发 | 使用难度 | 同时支持Xml/Wrapper/内置增删改查 | logic del逻辑删除插件| page分页插件 | ------ | ------ |------ |------ |------ |------ | | rbatis | √ | 非常简单 | √ | √ | √ |
| sqlx | √ | 难(强依赖宏和 莫名其妙的环境变量) | x | x | x |
| diesel | x | 简单(缺xml支持) | x | x | x |

和Go语言对比性能(环境(docker)仅供参考)

| 框架 | Mysql(docker) | SQL语句(1万次) | 纳秒/每操作(低越好) | Qps(高越好) |内存消耗(低越好) | | ------ | ------ |------ |------ |------ |------ | | Rust语言-rbatis/tokio | 1CPU,1G内存 | select count(1) from table; | 965649 ns/op | 1035 Qps/s | 2.1MB |
| Go语言-GoMybatis/http | 1CPU,1G内存 | select count(1) from table; | 1184503 ns/op | 844 Qps/s | 28.4MB |

实战(编写Rust后台服务) https://github.com/rbatis/abs_admin
使用方法:添加依赖(Cargo.toml)

``` rust

add this library,and cargo install

json支持(必须)

serde = { version = "1.0", features = ["derive"] } serde_json = "1.0"

日期支持(必须)

chrono = { version = "0.4", features = ["serde"] }

log日志支持(必须)

log = "0.4" fast_log="1.2.2"

BigDecimal支持(可选)

bigdecimal = "0.2"

rbatis支持,版本保持一致(必须)

rbatis-core = { version = "1.6.0", features = ["all"]} rbatis = { version = "1.6.0" } rbatis-macro-driver = { version = "1.6.0" }

```

一分钟快速学会, QueryWrapper,常用方法(详见example/crud_test.rs)

```rust

[macro_use]

extern crate rbatismacrodriver; ///数据库表模型 CRUDEnable也可以写成 impl CRUDEnable for BizActivity{}

[derive(CRUDEnable,Serialize, Deserialize, Clone, Debug)]

pub struct BizActivity { pub id: Option, pub name: Option, pub pclink: Option, pub h5link: Option, pub pcbannerimg: Option, pub h5bannerimg: Option, pub sort: Option, pub status: Option, pub remark: Option, pub createtime: Option, pub version: Option, pub deleteflag: Option, }

// (可选) 手动实现,不使用上面的derive(CRUDEnable),可重写tablename方法。手动实现能支持IDE智能提示 //impl CRUDEnable for BizActivity { // type IdType = String;
// fn table
name()->String{ // "bizactivity".tostring() // } // fn tablefields()->String{ // "id,name,deleteflag".to_string() // } //}

[actix_rt::main]

async fn main() { ///rbatis初始化,rbatis是线程安全可使用lazystatic 定义为全局变量 let rb = Rbatis::new(); ///连接数据库
rb.link("mysql://root:123456@localhost:3306/test").await.unwrap(); ///自定义连接池参数。(可选) // let mut opt =PoolOptions::new(); // opt.max
size=100; // rb.linkopt("mysql://root:123456@localhost:3306/test",&opt).await.unwrap(); ///新建的wrapper sql逻辑 let wrapper = rb.newwrapper() .eq("id", 1) //sql: id = 1 .and() //sql: and .ne("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("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 .order_by(true, &["id", "name"])//sql: group by id,name .check().unwrap();

let activity = BizActivity { id: Some("12312".tostring()), name: None, remark: None, createtime: Some(NaiveDateTime::now()), version: Some(1), deleteflag: Some(1), }; ///保存 rb.save("",&activity).await; //Exec ==> INSERT INTO bizactivity (createtime,deleteflag,h5bannerimg,h5link,id,name,pcbannerimg,pclink,remark,sort,status,version) VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )

///批量保存 rb.savebatch("", &vec![activity]).await; //Exec ==> INSERT INTO bizactivity (createtime,deleteflag,h5bannerimg,h5link,id,name,pcbannerimg,pclink,remark,sort,status,version) VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? ),( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )

///查询, Option包装,有可能查不到数据则为None let result: Option = rb.fetchbyid("", &"1".tostring()).await.unwrap(); //Query ==> SELECT createtime,deleteflag,h5bannerimg,h5link,id,name,pcbannerimg,pclink,remark,sort,status,version FROM bizactivity WHERE delete_flag = 1 AND id = ?

///查询-全部 let result: Vec = rb.list("").await.unwrap(); //Query ==> SELECT createtime,deleteflag,h5bannerimg,h5link,id,name,pcbannerimg,pclink,remark,sort,status,version FROM bizactivity WHERE deleteflag = 1

///批量-查询id let result: Vec = rb.listbyids("",&["1".tostring()]).await.unwrap(); //Query ==> SELECT createtime,deleteflag,h5bannerimg,h5link,id,name,pcbannerimg,pclink,remark,sort,status,version FROM bizactivity WHERE delete_flag = 1 AND id IN (?)

///自定义查询 let w = rb.newwrapper().eq("id", "1").check().unwrap(); let r: Result, Error> = rb.fetchbywrapper("", &w).await; //Query ==> SELECT createtime,deleteflag,h5bannerimg,h5link,id,name,pcbannerimg,pclink,remark,sort,status,version FROM bizactivity WHERE delete_flag = 1 AND id = ?

///删除 rb.removebyid::("", &"1".tostring()).await; //Exec ==> UPDATE bizactivity SET delete_flag = 0 WHERE id = 1

///批量删除 rb.removebatchbyid::("", &["1".tostring(), "2".tostring()]).await; //Exec ==> UPDATE bizactivity SET delete_flag = 0 WHERE id IN ( ? , ? )

///修改 let w = rb.newwrapper().eq("id", "12312").check().unwrap(); rb.updatebywrapper("", &activity, &w).await; //Exec ==> UPDATE bizactivity SET createtime = ? , deleteflag = ? , status = ? , version = ? WHERE id = ? }

///...还有更多方法,请查看crud.rs ```

智能宏映射(新功能)

```rust lazy_static! { static ref RB:Rbatis=Rbatis::new(); }

/// 宏根据方法定义生成执行逻辑,又点类似于 java/mybatis的@select动态sql
/// RB是本地依赖Rbatis引用的名称,例如  dao::RB, com::xxx::RB....都可以
/// 第二个参数是标准的驱动sql,注意对应数据库参数mysql为?,pg为$1...
/// 宏会自动转换函数为  pub async fn select(name: &str) -> rbatis_core::Result<BizActivity> {}
///
#[sql(RB, "select * from biz_activity where id = ?")]
fn select(name: &str) -> BizActivity {}
//其他写法: pub async fn select(name: &str) -> rbatis_core::Result<BizActivity> {}

#[async_std::test]
pub async fn test_macro() {
    fast_log::log::init_log("requests.log", &RuntimeType::Std);
    RB.link("mysql://root:123456@localhost:3306/test").await.unwrap();
    let a = select("1").await.unwrap();
    println!("{:?}", a);
}

rust lazy_static! { static ref RB:Rbatis=Rbatis::new(); }

/// 宏根据方法定义生成执行逻辑,又点类似于 java/mybatis的@select动态sql
/// RB是本地依赖Rbatis引用的名称,例如  dao::RB, com::xxx::RB....都可以
/// 第二个参数是标准的驱动sql,注意对应数据库参数mysql为?,pg为$1...
/// 宏会自动转换函数为  pub async fn select(name: &str) -> rbatis_core::Result<BizActivity> {}
///
#[py_sql(RB, "select * from biz_activity where id = #{name}
              if name != '':
                and name=#{name}")]
fn py_select(name: &str) -> Option<BizActivity> {}
//其他写法: pub async fn select(name: &str) -> rbatis_core::Result<BizActivity> {}

#[async_std::test]
pub async fn test_macro_py_select() {
    fast_log::log::init_log("requests.log", &RuntimeType::Std);
    RB.link("mysql://root:123456@localhost:3306/test").await.unwrap();
    let a = py_select("1").await.unwrap();
    println!("{:?}", a);
}

```

逻辑删除插件使用(逻辑删除针对Rbatis提供的查询方法和删除方法有效,例如方法 list(),remove(),fetch**())

rust let mut rb = init_rbatis().await; //rb.logic_plugin = Some(Box::new(RbatisLogicDeletePlugin::new_opt("delete_flag",1,0)));//自定义已删除/未删除 写法 rb.logic_plugin = Some(Box::new(RbatisLogicDeletePlugin::new("delete_flag"))); rb.link("mysql://root:123456@localhost:3306/test").await.unwrap(); let r = rb.remove_batch_by_id::<BizActivity>("", &["1".to_string(), "2".to_string()]).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(); //框架默认RbatisPagePlugin,如果需要自定义的话需要结构体 必须实现impl PagePlugin for Plugin*{},例如: //rb.page_plugin = Box::new(RbatisPagePlugin {});

    let req = PageRequest::new(1, 20);//分页请求,页码,条数
    let wraper= rb.new_wrapper()
                .eq("delete_flag",1)
                .check()
                .unwrap();
    let data: Page<BizActivity> = rb.fetch_page_by_wrapper("", &wraper,  &req).await.unwrap();
    println!("{}", serde_json::to_string(&data).unwrap());

//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", "pclink": "null", "h5link": "null", "pcbannerimg": "null", "h5bannerimg": "null", "sort": "null", "status": 1, "remark": "null", "createtime": "2020-02-09T00:00:00+00:00", "version": 1, "deleteflag": 1 }], "total": 5, "size": 20, "current": 1, "serch_count": true } ```

py风格sql语法Example

python //执行到远程mysql 并且获取结果。支持serde_json可序列化的任意类型 let rb = Rbatis::new(); rb.link("mysql://root:123456@localhost:3306/test").await.unwrap(); let py = r#" SELECT * FROM biz_activity WHERE delete_flag = #{delete_flag} if name != null: AND name like #{name+'%'} if ids != null: AND id in ( trim ',': for item in ids: #{item}, )"#; let data: serde_json::Value = rb.py_fetch("", py, &json!({ "delete_flag": 1 })).await.unwrap(); println!("{}", data);

日志系统(这里举例使用fast_log)

rust //main函数加入 use log::{error, info, warn}; fn main(){ fast_log::log::init_log("requests.log", &RuntimeType::Std).unwrap(); 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(); } ```

XML使用方法

``` rust /** * 数据库表模型 */

[derive(Serialize, Deserialize, Debug, Clone)]

pub struct Activity { pub id: Option, pub name: Option, pub pclink: Option, pub h5link: Option, pub pcbannerimg: Option, pub h5bannerimg: Option, pub sort: Option, pub status: Option, pub remark: Option, pub createtime: Option, pub version: Option, pub deleteflag: Option, } fn main() { asyncstd::task::blockon( async move { fastlog::log::initlog("requests.log", &RuntimeType::Std).unwrap(); let mut rb = Rbatis::new(); rb.link("mysql://root:123456@localhost:3306/test").await.unwrap(); //xml数据建议以 XXMapper.xml 的格式存储管理 rb.loadxml("test", r#" "#).unwrap();

           let arg = &json!({
           "delete_flag": 1,
           "name": "test",
           "startTime": null,
           "endTime": null,
           "page": 0,
           "size": 20
           });
           let data: Vec<BizActivity> = rb.xml_fetch("", "test", "select_by_condition", arg).await.unwrap();
           println!("{}", serde_json::to_string(&data).unwrap_or("".to_string()));
       }
   )

} //输出结果 //2020-06-27T03:13:40.422307200+08:00 INFO rbatis::rbatis - [rbatis] >> fetch sql: select * from bizactivity where name like ? order by createtime desc limit ? , ? (src\rbatis.rs:198) //2020-06-27T03:13:40.424307300+08:00 INFO rbatis::rbatis - [rbatis] >> fetch arg:["%test%",0,20] (src\rbatis.rs:199) //2020-06-27T03:13:40.446308900+08:00 INFO rbatis::rbatis - [rbatis] << 4 (src\rbatis.rs:234) //[{"id":"221","name":"test","pclink":"","h5link":"","pcbannerimg":null,"h5bannerimg":null,"sort":"0","status":0,"remark":"","createtime":"2020-06-17T20:10:23Z","version":0,"deleteflag":1},{"id":"222","name":"test","pclink":"","h5link":"","pcbannerimg":null,"h5bannerimg":null,"sort":"0","status":0,"remark":"","createtime":"2020-06-17T20:10:23Z","version":0,"deleteflag":1},{"id":"223","name":"test","pclink":"","h5link":"","pcbannerimg":null,"h5bannerimg":null,"sort":"0","status":0,"remark":"","createtime":"2020-06-17T20:10:23Z","version":0,"deleteflag":1},{"id":"178","name":"testinsret","pclink":"","h5link":"","pcbannerimg":null,"h5bannerimg":null,"sort":"1","status":1,"remark":"","createtime":"2020-06-17T20:08:13Z","version":0,"delete_flag":1}] ```

事务支持

rust async_std::task::block_on(async { let rb = Rbatis::new(); rb.link("mysql://root:123456@localhost:3306/test").await.unwrap(); let tx_id = "1";//事务id号 rb.begin(tx_id).await.unwrap(); let v: serde_json::Value = rb.fetch(tx_id, "SELECT count(1) FROM biz_activity;").await.unwrap(); println!("{}", v.clone()); rb.commit(tx_id).await.unwrap(); });

Web框架支持(这里举例actix-web,支持所有基于tokio,async_std的web框架)

``` rust lazy_static! { static ref RB:Rbatis=Rbatis::new(); }

async fn index() -> impl Responder { let v:Result = RB.fetch("", "SELECT count(1) FROM bizactivity;").await; HttpResponse::Ok().body(format!("count(1)={}",v.unwrapor(0))) }

[actix_rt::main]

async fn main() -> std::io::Result<()> { //日志 fastlog::log::initlog("requests.log", &RuntimeType::Std).unwrap(); //链接数据库 RB.link("mysql://root:123456@localhost:3306/test").await.unwrap(); //http路由 HttpServer::new(|| { App::new() .route("/", web::get().to(index)) }) .bind("127.0.0.1:8000")? .run() .await } ```

支持数据结构列表

| 数据库 | 已支持 | | ------ | ------ | | Option | √ | | Vec | √ |
| HashMap | √ |
| Slice | √ |
| i32,i64,f32,f64,bool,String...more rust type | √ |
| NativeDateTime | √ |
| BigDecimal | √ | | serde_json::Value...more serde type | √ |

支持数据库类型√已支持.进行中

| 数据库 | 已支持 | | ------ | ------ | | Mysql | √ |
| Postgres | √ |
| Sqlite | √ |
| TiDB | √ | | CockroachDB | √ |

平台测试通过

| 平台 | 已支持 | | ------ | ------ | | Linux | √ | | Apple/MacOS | √ |
| Windows | √ |

进度表-按照顺序实现

| 功能 | 已支持 | | ------ | ------ | | CRUD(内置CRUD模板(内置CRUD支持逻辑删除插件)) | √ | | LogSystem(日志组件) | √ | | Tx(事务/事务嵌套/注解声明式事务) | √ |
| Py(在SQL中使用和xml等价的类python语法) | √ | | SlowSqlCount(内置慢查询日志分析) | √ | | async/await支持 | √ | | PagePlugin(分页插件) | √ | | LogicDelPlugin(逻辑删除插件) | √ | | DataBaseConvertPlugin(数据库表结构转换为配置插件) | x | | web(可视化Web UI) | x |

基准测试benchmark (测试平台 win10,6 core i7,16GB)

分步骤压测

``` //sql构建性能 ExampleActivityMapper.xml -> selectby_condition 操作/纳秒nano/op: 0.202 s,each:2020 nano/op 事务数/秒 TPS: 495049.50495049503 TPS/s

//查询结果解码性能 decode/mysqljsondecoder -> benchdecodemysql_json 操作/纳秒nano/op: 0.24 s,each:2400 nano/op 事务数/秒 TPS: 416666.6666666667 TPS/s

//综合性能约等于 操作/纳秒nano/op: 4420 nano/op 事务数/秒 TPS: 200000 TPS/s ```

FAQ 常见问题

rbatis-core = { features = ["runtime-async-std","all-type"]}

或者Cargo.toml 加入

rbatis-core = { features = ["runtime-tokio","all-type"]}

```

和Rbatis相关项目

TODO 即将到来的特性

为了称心如意的ORM框架,您的支持永远是我们的动力,迫切欢迎微信捐赠支持我们 ~或者~右上角点下star

Image text