All Projects → rbatis → Rbatis

rbatis / Rbatis

Licence: apache-2.0
Rust ORM Framework High Performance Rust SQL-ORM(JSON based)

Programming Languages

rust
11053 projects

Projects that are alternatives of or similar to Rbatis

Ebean
Ebean ORM
Stars: ✭ 1,172 (+143.15%)
Mutual labels:  orm, mysql, sqlite, postgres
Xorm
Simple and Powerful ORM for Go, support mysql,postgres,tidb,sqlite3,mssql,oracle, Moved to https://gitea.com/xorm/xorm
Stars: ✭ 6,464 (+1241.08%)
Mutual labels:  orm, mysql, sqlite, postgres
Wetland
A Node.js ORM, mapping-based. Works with MySQL, PostgreSQL, SQLite and more.
Stars: ✭ 261 (-45.85%)
Mutual labels:  orm, mysql, sqlite, postgres
Prisma
Next-generation ORM for Node.js & TypeScript | PostgreSQL, MySQL, MariaDB, SQL Server, SQLite & MongoDB (Preview)
Stars: ✭ 18,168 (+3669.29%)
Mutual labels:  orm, mysql, postgres, sqlite
Xo
Command line tool to generate idiomatic Go code for SQL databases supporting PostgreSQL, MySQL, SQLite, Oracle, and Microsoft SQL Server
Stars: ✭ 2,974 (+517.01%)
Mutual labels:  orm, mysql, sqlite
Gnorm
A database-first code generator for any language
Stars: ✭ 415 (-13.9%)
Mutual labels:  orm, mysql, postgres
Requery
requery - modern SQL based query & persistence for Java / Kotlin / Android
Stars: ✭ 3,071 (+537.14%)
Mutual labels:  mysql, sqlite, postgres
Crecto
Database wrapper and ORM for Crystal, inspired by Ecto
Stars: ✭ 325 (-32.57%)
Mutual labels:  orm, mysql, postgres
Db
Data access layer for PostgreSQL, CockroachDB, MySQL, SQLite and MongoDB with ORM-like features.
Stars: ✭ 2,832 (+487.55%)
Mutual labels:  orm, mysql, sqlite
Node Orm2
Object Relational Mapping
Stars: ✭ 3,063 (+535.48%)
Mutual labels:  orm, mysql, sqlite
Sqlboiler
Generate a Go ORM tailored to your database schema.
Stars: ✭ 4,497 (+832.99%)
Mutual labels:  orm, mysql, postgres
Mikro Orm
TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, MariaDB, PostgreSQL and SQLite databases.
Stars: ✭ 3,874 (+703.73%)
Mutual labels:  orm, mysql, sqlite
Freesql
🦄 .NET orm, Mysql orm, Postgresql orm, SqlServer orm, Oracle orm, Sqlite orm, Firebird orm, 达梦 orm, 人大金仓 orm, 神通 orm, 翰高 orm, 南大通用 orm, Click house orm, MsAccess orm.
Stars: ✭ 3,077 (+538.38%)
Mutual labels:  orm, mysql, sqlite
Sqlsugar
Best ORM Fastest ORM Simple Easy Sqlite orm Oracle ORM Mysql Orm postgresql ORm SqlServer oRm 达梦 ORM 人大金仓 ORM 神通ORM C# ORM
Stars: ✭ 3,748 (+677.59%)
Mutual labels:  orm, mysql, sqlite
Pony
Pony Object Relational Mapper
Stars: ✭ 2,762 (+473.03%)
Mutual labels:  orm, mysql, sqlite
Architect
A set of tools which enhances ORMs written in Python with more features
Stars: ✭ 320 (-33.61%)
Mutual labels:  orm, mysql, postgres
Rdbc
Rust DataBase Connectivity (RDBC) :: Common Rust API for database drivers
Stars: ✭ 328 (-31.95%)
Mutual labels:  mysql, sqlite, postgres
Sqlx
🧰 The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, SQLite, and MSSQL.
Stars: ✭ 5,039 (+945.44%)
Mutual labels:  mysql, sqlite, postgres
Beam
A type-safe, non-TH Haskell SQL library and ORM
Stars: ✭ 454 (-5.81%)
Mutual labels:  orm, sqlite, postgres
Elefant
Elefant, the refreshingly simple PHP CMS and web framework.
Stars: ✭ 188 (-61%)
Mutual labels:  orm, mysql, sqlite

WebSite | 简体中文

Build Status doc.rs dependency status GitHub release

A highly Performant,Safe,Dynamic SQL ORM framework written in Rust, inspired by Mybatis and MybatisPlus.

Image text

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
  • used json with serde_json for passing parameters and communication
  • high performance, single threaded benchmark can easily achieve 200,000 QPS - data returned from database directly ( zero lookup time) on a Windows 10 6 core i7 with 16 GB memory machine. Performace will be better using multiple threads, and it outperforms Go's GoMyBatis.
  • supports logical deletes, pagination, py-like SQL and basic Mybatis functionalities.
  • supports future,(in theory, if all io operations are replaced with async_std/tokio, it could achieve higher concurrency than Go-lang)
  • supports logging, customizable logging based on log crate
  • used 100% safe Rust with #![forbid(unsafe_code)] enabled
  • rbatis/example (import into Clion!)
  • abs_admin project
Example Rust backend service https://github.com/rbatis/abs_admin
Example Cargo.toml
# add this library,and cargo install

# json (required)
serde = { version = "1", features = ["derive"] }
serde_json = "1"

# Date time (required)
chrono = { version = "0.4", features = ["serde"] }

# logging lib(required)
log = "0.4"
fast_log="1.3"

# BigDecimal lib(optional)
bigdecimal = "0.2"

# rbatis lib(required)
rbatis =  { version = "1.8" } 
Quick example: QueryWrapper and common usages (see example/crud_test.rs for details)
//#[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_enable( table_name:biz_activity)]
/// #[crud_enable(id_name:"id"|id_type:"String"|table_name:"biz_activity"|table_columns:"id,name,version,delete_flag"|formats_pg:"id:{}::uuid")]
#[crud_enable]
#[derive(Clone, Debug)]
pub struct BizActivity {
  pub id: Option<String>,
  pub name: Option<String>,
  pub pc_link: Option<String>,
  pub h5_link: Option<String>,
  pub pc_banner_img: Option<String>,
  pub h5_banner_img: Option<String>,
  pub sort: Option<String>,
  pub status: Option<i32>,
  pub remark: Option<String>,
  pub create_time: Option<NaiveDateTime>,
  pub version: Option<i32>,
  pub delete_flag: Option<i32>,
}

// (optional) manually implement instead of using `derive(CRUDTable)`. This allows manually rewriting `table_name()` function and supports  code completion in IDE.
// use rbatis::crud::CRUDTable;
//impl CRUDTable for BizActivity {
//    type IdType = String;    
//    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_log("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:[email protected]:3306/test").await.unwrap();
  /// customize connection pool parameters (optional)
// let mut opt =PoolOptions::new();
// opt.max_size=100;
// rb.link_opt("mysql://root:[email protected]: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("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("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,
    remark: None,
    create_time: Some(NaiveDateTime::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 ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? ),( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )

  /// The query, Option wrapper, is None if the data is not found
  let result: Option<BizActivity> = rb.fetch_by_id("", &"1".to_string()).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<BizActivity> = 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<BizActivity> = rb.list_by_ids("", &["1".to_string()]).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 w = rb.new_wrapper().eq("id", "1");
  let r: Result<Option<BizActivity>, Error> = rb.fetch_by_wrapper("", &w).await;
//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 =  ? 

  ///delete
  rb.remove_by_id::<BizActivity>("", &"1".to_string()).await;
//Exec ==> UPDATE biz_activity SET delete_flag = 0 WHERE id = 1

  ///delete batch
  rb.remove_batch_by_id::<BizActivity>("", &["1".to_string(), "2".to_string()]).await;
//Exec ==> UPDATE biz_activity SET delete_flag = 0 WHERE id IN (  ?  ,  ?  ) 

  ///update
  ///if version_lock plugin actived,update method will modify field 'version'= version + 1
  let mut activity = activity.clone();
  let w = rb.new_wrapper().eq("id", "12312");
  rb.update_by_wrapper("", &mut activity, &w).await;
//Exec ==> UPDATE biz_activity SET  create_time =  ? , delete_flag =  ? , status =  ? , version =  ?  WHERE id =  ? 
}

///...more usage,see crud.rs

macros (new addition)

#[macro_use]
extern crate lazy_static;

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

/// Other code writing way:
/// #[py_sql(RB, "select * from biz_activity where id = #{name}
///                 if name != '':
///                      and name=#{name}")]
/// pub async fn select(name: &str) -> rbatis::core::Result<BizActivity> {
///   println!("py_select name:{}",name);
/// }
#[py_sql(RB, "select * from biz_activity where id = #{name}
                  if name != '':
                    and name=#{name}")]
pub async fn py_select(name: &str) -> Option<BizActivity> {}

#[tokio::test]
pub async fn test_macro_py_select() {
    fast_log::init_log("requests.log", 1000, log::Level::Info, None, true);
    RB.link("mysql://root:[email protected]:3306/test").await.unwrap();
    let a = py_select("1").await.unwrap();
    println!("{:?}", a);
}
#[macro_use]
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<BizActivity> {}'
///
#[sql(RB, "select * from biz_activity where id = ?")]
pub async fn select(name: &str) -> BizActivity {}
//or: pub async fn select(name: &str) -> rbatis::core::Result<BizActivity> {}

#[tokio::test]
pub async fn test_macro() {
    fast_log::init_log("requests.log", 1000, log::Level::Info, None, true);
    RB.link("mysql://root:[email protected]:3306/test").await.unwrap();
    let a = select("1").await.unwrap();
    println!("{:?}", a);
}
How to use logical deletes plugin (works for fetching or removing functions provided by rbatis,e.g. list**(),remove**(),fetch**())
let mut rb:Rbatis=Rbatis::new();
//rb.logic_plugin = Some(Box::new(RbatisLogicDeletePlugin::new_opt("delete_flag",1,0)));//Customize deleted/undeleted writing
rb.logic_plugin = Some(Box::new(RbatisLogicDeletePlugin::new("delete_flag")));
rb.link("mysql://root:[email protected]: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());
}
How to use pagination plugin
let mut rb = Rbatis::new();
rb.link("mysql://root:[email protected]:3306/test").await.unwrap();
//replace page plugin
//rb.page_plugin = Box::new(RbatisPagePlugin::new());

let req = PageRequest::new(1, 20);
let wraper= rb.new_wrapper()
.eq("delete_flag", 1);
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 biz_activity  WHERE delete_flag =  ? 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  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 =  ? 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
{
  "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
}
py-like sql example
//Execute to remote mysql and get the result. Supports any serializable type of SERde_JSON
        let rb = Rbatis::new();
        rb.link("mysql://root:[email protected]: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);

logging system with fast_log here as an example

 use log::{error, info, warn};
 fn  main(){
      fast_log::init_log("requests.log", 1000, log::Level::Info, None, true);
      info!("print data");
 }

Customize connection pool's size, timeout, active number of connections, and etc.

use rbatis::core::db::PoolOptions;

pub async fn init_rbatis() -> Rbatis {
    let rb = Rbatis::new();
    let mut opt = PoolOptions::new();
    opt.max_size = 20;
    rb.link_opt("mysql://root:[email protected]:3306/test", &opt).await.unwrap();
}

Async/.await task support

        let rb = Rbatis::new();
        rb.link("mysql://root:[email protected]:3306/test").await.unwrap();
        let context_id = "tx:1";//事务id号
        rb.begin(context_id).await.unwrap();
        let v: serde_json::Value = rb.fetch(context_id, "SELECT count(1) FROM biz_activity;").await.unwrap();
        println!("{}", v.clone());
        rb.commit(context_id).await.unwrap();

How to use rbatis with Rust web frameworks (actix-web is used here as an example, but all web frameworks based on tokio or async_std are supported)

#[macro_use]
extern crate lazy_static;
lazy_static! {
   static ref RB:Rbatis=Rbatis::new();
}

async fn index() -> impl Responder {
    let v:Result<i32,rbatis::core::Error> = RB.fetch("", "SELECT count(1) FROM biz_activity;").await;
    HttpResponse::Ok().body(format!("count(1)={}",v.unwrap_or(0)))
}

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    //log
    fast_log::init_log("requests.log", 1000, log::Level::Info, None, true);
    //link database
    RB.link("mysql://root:[email protected]:3306/test").await.unwrap();
    //http server
    HttpServer::new(|| {
        App::new()
            .route("/", web::get().to(index))
    })
        .bind("127.0.0.1:8000")?
        .run()
        .await
}

Supported data structures

data structure is supported
Option
Vec
HashMap
Slice
i32,i64,f32,f64,bool,String...more rust type
NativeDateTime
BigDecimal
serde_json::Value...more serde type

Supported database √supported .WIP

database is supported
Mysql
Postgres
Sqlite
Mssql/Sqlserver
MariaDB(Mysql)
TiDB(Mysql)
CockroachDB(Postgres)

Supported OS/Platforms

platform is supported
Linux
Apple/MacOS
Windows

Progress - in sequential order

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
DataBase Table ConvertPage(Web UI,Coming soon) x
  • Conlusion: Assuming zero time consumed on IO, single threaded benchmark achieves 200K QPS or QPS, which is a few times more performant than GC languages like Go or Java.

FAQ

  • Postgres Types Define Please see Doc

中文

English Doc

  • Support for DateTime and BigDecimal?
    Currently supports chrono::NaiveDateTime和bigdecimal::BigDecimal
  • Supports for async/.await
    Currently supports both async_std and tokio
  • 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 select async/.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

changelog

changelog

Related Projects

In order to achieve the satisfaction of this ORM framework, your support is always our motivation, we are eager to welcome WeChat to donate to support us ~ or ~ star at the top right corner

微信捐赠支持我们 或者右上角点下star

Image text

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].