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

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

借鉴 Mybatis/GoMybatis/MybatisPlus

Build Status

Image text

为什么选择Rbatis

首先(Cargo.toml)添加项目依赖

``` rust

add this library,and cargo install

rbatis = "*" ```

py风格sql语法Example

python //执行到远程mysql 并且获取结果。支持serde_json可序列化的任意类型 let py_sql=" SELECT * FROM biz_activity if name!=null: name = #{name} AND delete_flag1 = #{delete_flag} if age!=1: AND age = 2 if age!=1: AND age = 3 trim 'AND ': AND delete_flag2 = #{delete_flag} WHERE id = '2';"; let data: Vec<Activity> = Rbatis::singleton() .py_sql("", &json!({ "name":"新人专享", "delete_flag": 1, }), py_sql) .unwrap();

日志系统(这里举例使用log4rs.yaml)

rust use log4rs::init_file; //main函数加入 fn main(){ log4rs::init_file("log4rs.yaml", Default::default()).unwrap(); }

定义log4rs.yaml

yaml refresh_rate: 1 seconds appenders: stdout: kind: console requests: kind: file path: "requests.log" encoder: pattern: "{d} - {m}{n}" root: level: info appenders: - stdout - requests

xml代码Example

xml <mapper> <result_map id="BaseResultMap"> <id column="id" property="id"/> <result column="name" property="name" lang_type="string"/> <result column="pc_link" property="pcLink" lang_type="string"/> <result column="h5_link" property="h5Link" lang_type="string"/> <result column="remark" property="remark" lang_type="string"/> <result column="version" property="version" lang_type="number" version_enable="true"/> <result column="create_time" property="createTime" lang_type="time"/> <result column="delete_flag" property="deleteFlag" lang_type="number" logic_enable="true" logic_undelete="1" logic_deleted="0"/> </result_map> <select id="select_by_condition" result_map="BaseResultMap"> <bind name="pattern" value="'%' + name + '%'"/> select * from biz_activity <where> <if test="name != null">and name like #{pattern}</if> <if test="startTime != null">and create_time >= #{startTime}</if> <if test="endTime != null">and create_time &lt;= #{endTime}</if> </where> order by create_time desc <if test="page != null and size != null">limit #{page}, #{size}</if> </select> </mapper>

Cargo.toml 加入以下代码

toml [dependencies] rbatis = "*" log = "0.4" log4rs = "0.8.3"

简单使用

``` rust use rbatis::rbatis::Rbatis; use rbatis::error::RbatisError;

fn main() { //first install log log4rs::initfile("log4rs.yaml", Default::default()).unwrap(); // you may need install your mysql or change database url. Rbatis::singleton().dbdriver = "mysql://root:123456@127.0.0.1:3306/test".tostring(); let data:Result=Rbatis::singleton().rawsql("","select * from biz_activity;"); println!("{}",data.ok().unwrap()); } ```

xml使用方法

``` rust use crate::core::rbatis::Rbatis; use serde_json::{json, Value, Number}; /** * 数据库表模型 */

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

pub struct Activity { pub id: Option, pub name: Option, pub version: Option, }

fn main() { log4rs::initfile("log4rs.yaml", Default::default()).unwrap();//1 启用日志(可选,不添加则不加载日志库) let mut rbatis = Rbatis::new();//2 初始化rbatis,也可以使用全局单例Rbatis::singleton() rbatis.loaddburl("mysql://root:TEST@localhost:3306/test");//3 加载数据库url rbatis.loadxml("ExampleActivityMapper.xml".tostring(),fs::readtostring("./src/example/ExampleActivityMapper.xml").unwrap());//4 加载xml配置 let dataresult: Vec =rbatis.eval("".tostring(), "selectbycondition", &json!({ "name":null, "startTime":null, "endTime":null, "page":null, "size":null, })).unwrap(); println!("[rbatis] result==> {:?}",dataresult); } //输出结果 //2020-01-10T10:28:54.437167+08:00 INFO rbatis::core::rbatis - [rbatis] Query ==> ExampleActivityMapper.xml.selectbycondition: select * from bizactivity order by createtime desc //2020-01-10T10:28:54.437306+08:00 INFO rbatis::core::rbatis - [rbatis][args] ==> ExampleActivityMapper.xml.selectbycondition: //2020-01-10T10:28:54.552097+08:00 INFO rbatis::core::rbatis - [rbatis] ReturnRows <== 2 //[rbatis] result==> [Activity { id: Some("\"dfbdd779-5f70-4b8f-9921-a235a9c75b69\""), name: Some("\"新人专享\""), version: Some(6) }, Activity { id: Some("\"dfbdd779-5f70-4b8f-9921-c235a9c75b69\""), name: Some("\"新人专享\""), version: Some(6) }] ```

事务支持

``` rust

//自定义事务
pub fn tx() -> Result<u32,RbatisError>{
    let tx_id="1234";//事务id
    Rbatis::singleton().begin(tx_id, Propagation::REQUIRED)?;//启动事务,传入事务传播行为
    let affected: u32 = Rbatis::singleton()
        .raw_sql(tx_id, "UPDATE `biz_activity` SET `name` = '活动1' WHERE `id` = '2'")?;
    Rbatis::singleton().commit(tx_id)?;//提交事务
    Rbatis::singleton().rollback(tx_id)?;//回滚事务
    Ok(affected)
}
//声明式事务
pub trait Service {
    fn select_activity(&self) -> Result<Activity, RbatisError>;
    fn update_activity(&mut self) -> Result<String, RbatisError>;
}
struct ServiceImpl {
    select_activity: fn(s: &ServiceImpl) -> Result<Activity, RbatisError>,
    update_activity: fn(s: &mut ServiceImpl) -> Result<String, RbatisError>,
}
impl Service for ServiceImpl {
    impl_service! {
      REQUIRED,  select_activity(&self) -> Result<Activity,RbatisError>
    }
    impl_service_mut! {
      NONE,  update_activity(&mut self) -> Result<String, RbatisError>
    }
}
#[test]
pub fn test_service() {
    let mut s = ServiceImpl {
        select_activity: |s: &ServiceImpl| -> Result<Activity, RbatisError>{
            let act: Activity = Rbatis::singleton().raw_sql("", "select * from biz_activity where id  = '2';").unwrap();
            return Result::Ok(act);
        },
        update_activity: |s: &mut ServiceImpl| -> Result<String, RbatisError>{
            return Result::Ok("ok".to_string());
        },
    };
    let act: Activity = s.select_activity().unwrap();
    println!("{:?}", serde_json::to_string(&act).unwrap().as_str());
    println!("{:?}", s.update_activity().unwrap());
}

```

Web框架支持(这里举例actix-web,不支持tokio的框架使用 默认方法,支持tokio的使用async_* 开头的方法)

``` rust //这里举例使用web排行榜屠榜最快的actix-web

[macro_use]

use rbatis::rbatis_macro;

async fn index() -> impl Responder { //写法 let data: Result = Rbatis::asyncrawsql("", "select * from bizactivity where id = '2';").await; println!("{:?}", &data); return serdejson::to_string(&data).unwrap(); }

[actix_rt::main]

async fn main() -> std::io::Result<()> { //1 启用日志(可选,不添加则不加载日志库) log4rs::initfile("log4rs.yaml", Default::default()).unwrap(); //2 加载数据库url name 为空,则默认数据库 Rbatis::singleton().loaddburl(MYSQLURL);//"mysql://root:TEST@localhost:3306/test" //3 加载xml配置 let f = fs::File::open("./src/example/ExampleActivityMapper.xml"); Rbatis::singleton().loadxml("ExampleActivityMapper.xml".tostring(), fs::readtostring("./src/example/Example_ActivityMapper.xml").unwrap());//加载xml数据 //初始化rbatis HttpServer::new(move || { App::new() .route("/", web::get().to(index)) }) .bind("127.0.0.1:8000")? .run() .await } ```

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

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

进度表-按照顺序实现

| 功能 | 已支持 | | ------ | ------ | | CRUD(内置CRUD模板(内置CRUD支持乐观锁/逻辑删除)) | √ | | LogSystem(日志组件) | √ | | LogicDelPlugin(逻辑删除插件) | √ | | VersionLockPlugin(乐观锁插件,防止并发修改数据) | √ | | PagePlugin(分页插件) | √ | | Tx(事务/事务嵌套/注解声明式事务) | √ |
| Py(在SQL中使用和xml等价的类python语法) | √ | | SlowSqlCount(内置慢查询日志分析) | √ | | async/await支持(actix/actix-web,hyper等等兼容Tokio的web框架) | √ | | 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 ```

欢迎右上角点下 star 或者 微信 捐赠 和 赞助~

Image text