Skip to main content

Crate rbatis

Crate rbatis 

Source
Expand description

§Rbatis

§📖 English Documentation | 📖 中文文档

Website | Showcase | Examples

Build Status doc.rs unsafe forbidden codecov GitHub release discussions

§Introduction

Rbatis is a high-performance ORM framework for Rust based on compile-time code generation. It perfectly balances development efficiency, performance, and stability, functioning as both an ORM and a dynamic SQL compiler.

§More Supported

§Core Advantages

§1. High Performance

  • 🚀 Dynamic SQL to Native Rust Translation: The key advantage - compiles dynamic SQL directly to native Rust code, achieving performance equivalent to hand-written SQL
  • Zero Runtime Overhead: All SQL parsing and optimization happens at compile-time, eliminating runtime interpretation costs
  • Based on Tokio Async Model: Fully utilizes Rust’s async features to enhance concurrency performance
  • Efficient Connection Pools: Built-in multiple connection pool implementations, optimizing database connection management

§2. Reliability

  • Rust Safety Features: Leverages Rust’s ownership and borrowing checks to ensure memory and thread safety
  • Unified Parameter Placeholders: Uses ? as a unified placeholder, supporting all drivers
  • Two Replacement Modes: Precompiled #{arg} and direct replacement ${arg}, meeting different scenario requirements

§3. Development Efficiency

  • Powerful ORM Capabilities: Automatic mapping between database tables and Rust structures
  • Multiple SQL Building Methods:
    • py_sql: Python-style dynamic SQL with if, for, choose/when/otherwise, bind, trim structures and collection operations (.sql(), .csv())
    • html_sql: MyBatis-like XML templates with familiar tag structure (<if>, <where>, <set>, <foreach>), declarative SQL building, and automatic handling of SQL fragments without requiring CDATA
    • Raw SQL: Direct SQL statements
  • CRUD Macros: Generate common CRUD operations with a single line of code
  • Interceptor Plugin: Custom extension functionality
  • Table Sync Plugin: Automatically create/update table structures

§4. Extensibility

  • Multiple Database Support: MySQL, PostgreSQL, SQLite, MSSQL, MariaDB, TiDB, CockroachDB, Oracle, TDengine, etc.
  • Custom Driver Interface: Implement a simple interface to add support for new databases
  • Multiple Connection Pools: FastPool (default), Deadpool, MobcPool
  • Compatible with Various Web Frameworks: Seamlessly integrates with ntex, actix-web, axum, hyper, rocket, tide, warp, salvo, and more

§Supported Database Drivers

§Supported Connection Pools

§Supported Data Types

Data TypeSupport
Option✓
Vec✓
HashMap✓
i32, i64, f32, f64, bool, String, and other Rust base types✓
rbatis::rbdc::types::{Bytes, Date, DateTime, Time, Timestamp, Decimal, Json}✓
rbatis::plugin::page::{Page, PageRequest}✓
rbs::Value✓
serde_json::Value and other serde types✓
Driver-specific types from rbdc-mysql, rbdc-pg, rbdc-sqlite, rbdc-mssql✓

§Member crates

crateGitHub Link
rbdcrbdc
rbsrbs

§How Rbatis Works

Rbatis uses compile-time code generation through the rbatis-codegen crate, which means:

  1. 🎯 Direct Translation to Native Rust: Dynamic SQL is converted to optimized Rust code during compilation, achieving performance identical to hand-written SQL without any runtime interpretation overhead.

  2. Compilation Process:

    • Lexical Analysis: Handled by func.rs in rbatis-codegen using Rust’s syn and quote crates
    • Syntax Parsing: Performed by parser_html and parser_pysql modules in rbatis-codegen
    • Abstract Syntax Tree: Built using structures defined in the syntax_tree package in rbatis-codegen
    • Intermediate Code Generation: Executed by func.rs, which contains all the code generation functions
  3. Build Process Integration: The entire process runs during the cargo build phase as part of Rust’s procedural macro compilation. The generated code is returned to the Rust compiler for LLVM compilation to produce machine code.

  4. Compile-Time SQL Optimization: Unlike traditional ORMs that interpret dynamic SQL at runtime (causing performance penalties), Rbatis translates SQL to native Rust code at compile-time, delivering hand-written SQL performance while maintaining ORM convenience.

§Performance Benchmarks

---- bench_raw stdout ----(windows/SingleThread)
Time: 52.4187ms ,each:524 ns/op
QPS: 1906435 QPS/s

---- bench_select stdout ----(macos-M1Cpu/SingleThread)
Time: 112.927916ms ,each:1129 ns/op
QPS: 885486 QPS/s

---- bench_insert stdout ----(macos-M1Cpu/SingleThread)
Time: 346.576666ms ,each:3465 ns/op
QPS: 288531 QPS/s

§Quick Start

§Dependencies

# Cargo.toml
[dependencies]
#drivers
rbdc-sqlite = { version = "4" }
# rbdc-mysql = { version = "4" }
# rbdc-pg = { version = "4" }
# rbdc-mssql = { version = "4" }
rbs = { version = "4"}
rbatis = { version = "4.8"}

# Other dependencies
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
log = "0.4"
fast_log = "1.6"

§Basic Usage

use rbatis::rbdc::datetime::DateTime;
use rbs::value;
use rbatis::crud;
use rbatis::RBatis;
use rbdc_sqlite::SqliteDriver;
use serde::{Deserialize, Serialize};
use serde_json::json;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Activity {
    pub id: Option<String>,
    pub name: Option<String>,
    pub create_time: Option<DateTime>
}

// Automatically generate CRUD methods
crud!(Activity{});

#[tokio::main]
async fn main() {
    // Configure logging (optional, any logger works)
    // fast_log::init(fast_log::Config::new().console()).expect("log init fail");
    
    // Initialize rbatis
    let rb = RBatis::new();
    
    // Connect to database
    rb.init(SqliteDriver {}, "sqlite://target/sqlite.db").expect("rb init fail");
    // /// other databases
    // rb.init(PgDriver{}, "postgres://postgres:123456@localhost:5432/postgres").expect("pool init fail");
    // rb.init(MysqlDriver{}, "mysql://root:123456@localhost:3306/test").expect("pool init fail");
    
    // Create data
    let activity = Activity {
        id: Some("1".into()),
        name: Some("Test Activity".into()),
        create_time: Some(DateTime::now()),
    };
    let arrays = vec![
        Activity {
            id: Some("2".into()),
            name: Some("Activity 2".into()),
            create_time: Some(DateTime::now())
        },
        Activity {
            id: Some("3".into()),
            name: Some("Activity 3".into()),
            create_time: Some(DateTime::now())
        },
    ];

    // Insert data
    let data = Activity::insert(&rb, &activity).await;

    // Batch insert
    let data = Activity::insert_batch(&rb, &arrays, 10).await;

    // Update by map condition (updates all fields)
    let data = Activity::update_by_map(&rb, &activity, value!{ "id": "1" }).await;

    // Update only specific columns using the "column" key in condition (GitHub issue #591)
    let data = Activity::update_by_map(&rb, &activity, value!{ "id": "1", "column": ["name", "status"] }).await;

    // Query by map condition
    let data = Activity::select_by_map(&rb, value!{"id":"2","name":"Activity 2"}).await;

    // LIKE query
    let data = Activity::select_by_map(&rb, value!{"name like ":"%Activity%"}).await;

    // Greater than query
    let data = Activity::select_by_map(&rb, value!{"id > ":"2"}).await;

    // IN query
    let data = Activity::select_by_map(&rb, value!{"id": &["1", "2", "3"]}).await;

    // Delete by map condition
    let data = Activity::delete_by_map(&rb, value!{"id": &["1", "2", "3"]}).await;
}

§Advanced Usage (html_sql)

Use #[rbatis::html_sql("html/file_path")] macro for complex queries like pagination, join queries, etc.:

ⓘ
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct Activity {
    pub id: Option<String>,
    pub name: Option<String>
}
#[rbatis::html_sql("example/example.html")]
impl Activity {
    // Paginated query (PageIntercept handles limit/offset automatically)
    pub async fn select_by_page(rb: &dyn rbatis::Executor, page_req: &rbatis::PageRequest, name: &str) -> rbatis::Result<rbatis::Page<Activity>> {impled!()}
    pub async fn select_by_condition(rb: &dyn rbatis::Executor,name: &str) -> rbatis::Result<Vec<Activity>> {impled!()}
    pub async fn update_by_id(rb: &dyn rbatis::Executor,arg: &Activity) -> rbatis::Result<rbatis::rbdc::ExecResult> {impled!()}
    pub async fn delete_by_id(rb: &dyn rbatis::Executor,id: &str) -> rbatis::Result<rbatis::rbdc::ExecResult> {impled!()}
}

Corresponding HTML template file example/example.html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "https://raw.githubusercontent.com/rbatis/rbatis/master/rbatis-codegen/mybatis-3-mapper.dtd">
<mapper>
 <select id="select_by_page">
    SELECT * FROM activity
    <where>
        <if test="name != ''">
            AND name LIKE #{name}
        </if>
    </where>
 </select>
 <select id="select_by_condition">
    SELECT * FROM activity
    <where>
        <if test="name != ''">
            AND name LIKE #{name}
        </if>
    </where>
 </select>
 <update id="update_by_id">
        ` update activity `
        <set collection="arg"></set>
        ` where id = #{id} `
 </update>
 <delete id="delete_by_id">
    DELETE FROM activity WHERE id = #{id}
 </delete>   
</mapper>

Applicable scenarios: paginated queries, join queries, complex dynamic SQL, multi-condition search

§Creating a Custom Database Driver

To implement a custom database driver for Rbatis:

  1. Define your driver project with dependencies:
[features]
default = ["tls-rustls"]
tls-rustls=["rbdc/tls-rustls"]
tls-native-tls=["rbdc/tls-native-tls"]
[dependencies]
rbs = { version = "4"}
rbdc = { version = "4", default-features = false, optional = true }
fastdate = { version = "0.3" }
tokio = { version = "1", features = ["full"] }
  1. Implement the required traits:
ⓘ
use rbdc::db::{Driver, MetaData, Row, Connection, ConnectOptions, Placeholder};

pub struct YourDriver{}
impl Driver for YourDriver{}

pub struct YourMetaData{}
impl MetaData for YourMetaData{}

pub struct YourRow{}
impl Row for YourRow{}

pub struct YourConnection{}
impl Connection for YourConnection{}

pub struct YourConnectOptions{}
impl ConnectOptions for YourConnectOptions{}

pub struct YourPlaceholder{}
impl Placeholder for YourPlaceholder{}

// Then use your driver:
#[tokio::main]
async fn main() -> Result<(), rbatis::Error> {
  let rb = rbatis::RBatis::new();
  rb.init(YourDatabaseDriver {}, "database://username:password@host:port/dbname")?;
}

§Contact Us

discussions

§Donations or Contact

zxj347284221

WeChat (Please note ‘rbatis’ when adding as friend)

zxj347284221

§API guide

The pages below are the entry points for the runtime API. Most users only need RBatis (the engine) plus the crud! macro.

ModuleWhat you find there
rbatisRBatis: create the engine, init/link a driver, acquire connections and transactions
executorExecutor and RBatisRef: every handle that can run SQL (RBatis, RBatisConnExecutor, RBatisTxExecutor, RBatisTxExecutorGuard)
crudthe crud! macro, which generates insert, insert_batch, select_by_map, update_by_map, delete_by_map for a struct
crud_traitssmall traits used by the generated CRUD code
pluginpagination (plugin::page), SQL interceptors (plugin::intercept), distributed ID generators (plugin::id_generator) and table sync (plugin::table_sync)
decodedecode rbs::Value rows into your own serde types (decode(), decode_ref())
errorError and Result used by every API of this crate
utilshelper macros: field_name!, field_key!, table!, table_field_vec!, table_field_map!, table_field_set!, table_field_btree!

Attribute macros live in this crate root as well: py_sql, html_sql and sql turn a function body into an executable SQL statement at compile time, impled! is the placeholder body used together with them.

§Feature flags

FeatureDefaultEffect
defaultyesenables the procedural macro driver, so #[py_sql], #[html_sql], #[sql], crud!, table! … are available
debug_modenoprints the JSON rows returned by the database and the generated dynamic SQL code to the log/fast_log output. They are printed with the debug log level, so a logger must be initialized with LevelFilter::Debug
println_gennoprints the Rust code generated by #[py_sql]/#[html_sql] at compile time (useful when debugging a query)
upper_case_sql_keywordnoreserved; currently a no-op

§Documentation

Re-exports§

pub extern crate dark_std;
pub extern crate rbatis_codegen;
pub extern crate rbdc;

Re-exports§

pub use decode::*;
pub use error::*;
pub use executor::*;
pub use plugin::*;
pub use rbatis::*;

Modules§

crud
The crud! macro, which generates the standard CRUD methods for a table struct. The crud! macro and the pagination helpers built on top of py_sql/html_sql.
crud_traits
Traits used by the code generated from crud!.
decode
Types and traits for decoding values from the database. Decoding rows into your own types.
error
Error and Result. The error type used by every rbatis API.
executor
Traits describing anything that can execute SQL: Executor and RBatisRef. Everything that can run SQL.
plugin
Plugins shipped with rbatis: pagination, SQL interceptors, table sync and ID generators.
rbatis
The rbatis engine (RBatis) and its connection/transaction types.
utils
Helper macros used by the generated code (field_name!, table!, impled!, …). Helper macros, mostly used by the code generated from crud! and the SQL attribute macros.

Macros§

crud
PySql: gen insert/insert_batch, select_by_map, update_by_map, delete_by_map methods
field_key
Used to simulate enumerations to improve code maintainability. this is return &str data for example:
field_name
Used to simulate enumerations to improve code maintainability. this is return &str data for example:
htmlsql
Declare a function whose body is filled in by the #[html_sql] attribute macro.
htmlsql_select_page
Generate a paginated function from an html_sql template.
impled
Placeholder body for functions handled by #[py_sql], #[html_sql] or #[sql].
pysql
Declare a function whose body is filled in by the #[py_sql] attribute macro.
pysql_select_page
Generate a paginated function from a py_sql template.
raw_sql
Declare a function whose body is filled in by the #[sql] attribute macro.
table
Simplifies table construction by relying on the Default trait
table_field_btree
Gets the BtreeMap collection of member attributes of the target Vec for example:
table_field_map
Gets the HashMap collection of member attributes of the target Vec for example:
table_field_set
take the target Vec member attribute Vec collection for example:
table_field_vec
take the target Vec member attribute Vec collection for example:

Structs§

DefaultPool

Attribute Macros§

async_trait
html_sql
html sql create macro,this macro use RB.py_query and RB.py_exec for example:
py_sql
py sql create macro,this macro use RB.py_query and RB.py_exec
snake_name
turn a function declaration into a function that returns the snake_case name of the type given as the first argument:
sql
auto create sql macro,this macro use RB.query_prepare and RB.exec_prepare for example: