# rust-rbatis
**Repository Path**: sephiro/rust-rbatis
## Basic Information
- **Project Name**: rust-rbatis
- **Description**: rbatis 是一个用 Rust 编写的高性能、安全、动态 SQL(编译时)ORM 框架,受 Mybatis 和 MybatisPlus 的启发
- **Primary Language**: Rust
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: https://www.oschina.net/p/rust-rbatis
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 6
- **Created**: 2022-05-20
- **Last Updated**: 2022-05-20
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
[WebSite](https://rbatis.github.io/rbatis.io/#/en/) | [简体中文](https://rbatis.github.io/rbatis.io/) |
[Showcase](WhoUse.md) | [案例](WhoUse.md)
[](https://travis-ci.org/zhuxiujia/rbatis)
[](https://docs.rs/rbatis/)
[](https://crates.io/crates/rbatis)
[](https://github.com/rust-secure-code/safety-dance/)
[](https://deps.rs/crate/rbatis/1.8.71)
[](https://github.com/rbatis/rbatis/releases)
[](https://gitter.im/rbatis_orm/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
#### A highly Performant,Safe,Dynamic SQL(Compile time) ORM framework written in Rust, inspired by Mybatis and MybatisPlus.
##### Why not diesel or not sqlx ?
| 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 |
##### Performance comparison with Golang (in a docker environment)
| 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 |
* No Runtimes,No Garbage Collection
* Zero cost [Dynamic SQL](dyn_sql.md), implemented using (proc-macro,compile-time,Cow(Reduce unnecessary cloning))
techniques。 don't need ONGL engine(mybatis)
* Free deserialization, Auto Deserialize to any struct(Option,Map,Vec...)
* High performance, Based on Future, with async_std/tokio, single threaded benchmark can easily achieve 200,000 QPS
* logical deletes, pagination, py-like SQL and basic Mybatis functionalities.
* Supports logging, customizable logging based on `log` crate
* 100% Safe Rust with `#![forbid(unsafe_code)]` enabled
* [rbatis/example (import into Clion!)](example/src)
* [abs_admin project](https://github.com/rbatis/abs_admin) an complete background user management system(
Vue.js+rbatis+actix-web)
### Supported data structures
| 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, PageRequest} | √ |
| rbson::Bson* | √ |
| serde_json::* | √ |
| any serde type | √ |
### Supported database √supported .WIP
| database | is supported |
| ------ | ------ |
| Mysql | √ |
| Postgres | √ |
| Sqlite | √ |
| Mssql/Sqlserver | √[(50%)](https://github.com/launchbadge/sqlx/issues/414) |
| MariaDB(Mysql) | √ |
| TiDB(Mysql) | √ |
| CockroachDB(Postgres) | √ |
### Supported OS/Platforms
| platform | is supported |
| ------ | ------ |
| Linux | √ |
| Apple/MacOS | √ |
| Windows | √ |
### Supported Web Frameworks
* [actix-web](example/src/actix_web/main.rs)
* [axum](example/src/axum/main.rs)
* [hyper](example/src/hyper/main.rs)
* [ntex](example/src/ntex/main.rs)
* [rocket](example/src/rocket/main.rs)
* [tide](example/src/tide/main.rs)
* [warp](example/src/warp/main.rs)
* [salvo](example/src/salvo/main.rs)
##### Quick example: QueryWrapper and common usages (see example/crud_test.rs for details)
* Cargo.toml
``` rust
# 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"] }
```
```rust
//#[macro_use] define in 'root crate' or 'mod.rs' or 'main.rs'
#[macro_use]
extern crate 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)]
pub struct BizActivity {
pub id: Option,
pub name: Option,
pub pc_link: Option,
pub h5_link: Option,
pub pc_banner_img: Option,
pub h5_banner_img: Option,
pub sort: Option,
pub status: Option,
pub remark: Option,
pub create_time: Option,
pub version: Option,
pub delete_flag: Option,
}
// 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]
async fn main() {
/// enable log crate to show sql logs
fast_log::init(fast_log::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.max_size=100;
// rb.link_opt("mysql://root:123456@localhost:3306/test",&opt).await.unwrap();
/// newly constructed wrapper sql logic
let 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 = 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 all
let result: Vec = 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 vec
let result: Vec = 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 wrapper
let r: Result