Releases · rbatis/rbatis
v4.8.0
what changes?
Breaking Changes
-
remove impl_insert!, impl_select!, impl_update!, impl_delete! - must use single
crud!macro instead- previously you needed 4 separate macros to enable CRUD operations
- now only
crud!(Table{})is needed to enableinsert,insert_batch,select_by_map,update_by_map,delete_by_map
-
remove custom query methods via py_sql expand - now only use
*_by_map()methods- previously you could define custom query methods like
select_by_id(id: String) -> Option => "where id = #{id} limit 1" - now all queries must use
select_by_map()with condition map
- previously you could define custom query methods like
-
remove select_all() - use
select_by_map(rb, value!{})instead- previously:
Table::select_all(rb).await - now:
Table::select_by_map(rb, value!{}).await
- previously:
-
remove impl_select_page! macro - use
select_by_map()with pagination logic in application code
The simplifications mentioned above are all aimed at maintaining conciseness, so as to facilitate its use by AI agents
Upgrade Guide
Old code (v4.7):
rbatis::impl_insert!(MockTable{}); rbatis::impl_select!(MockTable{}); rbatis::impl_update!(MockTable{}); rbatis::impl_delete!(MockTable{}); let _ = MockTable::insert(rb, &table).await; let _ = MockTable::select_all(rb).await; let _ = MockTable::update_by_map(rb, &table, value!{"id":"1"}).await; let _ = MockTable::delete_by_map(rb, value!{"id":"1"}).await;
New code (v4.8):
rbatis::crud!(MockTable{}); let result: ExecResult = MockTable::insert(rb, &table).await?; let tables: Vec<MockTable> = MockTable::select_by_map(rb, value!{}).await?; let result: ExecResult = MockTable::update_by_map(rb, &table, value!{"id":"1"}).await?; let result: ExecResult = MockTable::delete_by_map(rb, value!{"id":"1"}).await?;
v4.7.2
what changes?
- remove #[html_sql] for mod
- add Page support for #[html_sql] for struct
for example:
#[rbatis::html_sql("example/example.html")] impl Activity { /// Paginated query - automatically detected by return type Page<Activity> /// Maps to <select id="select_by_page"> in HTML /// The macro automatically generates pagination logic using PageIntercept pub async fn select_by_page( rb: &dyn Executor, page_req: &dyn PageRequest, name: &str, dt: Option<DateTime>, ) -> rbatis::Result<rbatis::Page<Activity>> { impled!() } }
<!-- Paginated query - automatically detected by return type Page<Activity> --> <select id="select_by_page"> `select * from activity` <where> <if test="name != ''"> ` and name like #{name}` </if> <if test="dt != null"> ` and create_time < #{dt}` </if> </where> <!-- PageIntercept will add limit/offset automatically --> </select>
v4.7.1
what changes?
- merge #609 for struct/mod html_sql mapping
for example:
use rbatis::dark_std::defer; use rbatis::executor::Executor; use rbatis::rbdc::datetime::DateTime; use rbatis::RBatis; #[derive(serde::Serialize, serde::Deserialize, Debug)] pub struct Activity { 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<DateTime>, pub version: Option<i64>, pub delete_flag: Option<i32>, } /// All methods read SQL from example/example.html #[rbatis::html_sql("example/example.html")] impl Activity { /// Maps to <select id="select_by_condition"> in HTML pub async fn select_by_condition( rb: &dyn Executor, name: &str, dt: &DateTime, ) -> rbatis::Result<Vec<Activity>> { impled!() } /// Maps to <select id="select_page_data"> in HTML pub async fn select_page_data( rb: &dyn Executor, name: &str, dt: &DateTime, ) -> rbatis::Result<Vec<Activity>> { impled!() } /// Maps to <update id="update_by_id"> in HTML pub async fn update_by_id( rb: &dyn Executor, arg: &Activity, ) -> rbatis::Result<rbatis::rbdc::db::ExecResult> { impled!() } }
<!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_condition"> `select * from activity` <where> <if test="name != ''"> ` and name like #{name}` </if> <if test="dt >= '2009-12-12 00:00:00'"> ` and create_time < #{dt}` </if> <choose> <when test="true"> ` and id != '-1'` </when> <otherwise>and id != -2</otherwise> </choose> ` and ` <trim prefixOverrides=" and"> ` and name != '' ` </trim> </where> </select> <select id="select_page_data"> `select * from activity ` <where> <if test="name != ''"> ` and name like #{name}` </if> <if test="dt >= '2009-12-12 00:00:00'"> ` and create_time < #{dt}` </if> <choose> <when test="true"> ` and id != '-1'` </when> <otherwise>and id != -2</otherwise> </choose> ` and ` <trim prefixOverrides=" and"> ` and name != '' ` </trim> </where> </select> <update id="update_by_id"> ` update activity ` <set collection="arg"></set> ` where id = #{id} ` </update> </mapper>
v4.7.0
what changes?
- RBatis Engine All use dyn Trait for example:
pub struct RBatis { // the connection pool pub pool: Arc<OnceLock<Box<dyn Pool>>>, // intercept vec(default the intercepts[0] is a log interceptor) pub intercepts: Arc<SyncVec<Arc<dyn Intercept>>>, //rb task id gen pub task_id_generator: Arc<dyn IdGenerator>, }
- performance
| Optimization Item | Save Time | |---------------------------------|-----------| | Avoid `vec![]` allocation | ~50 ns | | Avoid creating RBAtisKonExecutor| ~100 ns | | Avoid `task_id` generation | ~11 ns | | Avoid `Arc` cloning | ~20 ns | | Reduce function call hierarchy | ~50 ns | | Empty string fast path | ~30 ns | | **Total** | **~260 ns** |
- intercept add Action
use rbatis::Error; use rbatis::executor::Executor; use rbatis::intercept::{Intercept, ResultType}; use rbdc::db::ExecResult; use rbs::Value; use rbatis::Action; #[derive(Debug)] pub struct ReturningIdPlugin{} #[rbatis::async_trait] impl Intercept for ReturningIdPlugin { async fn before( &self, _task_id: i64, rb: &dyn Executor, sql: &mut String, args: &mut Vec<Value>, result: ResultType<&mut Result<ExecResult, Error>, &mut Result<Value, Error>>, ) -> Result<Action, Error> { Ok(Action::Next) } }
v4.6.15
- up rbdc version
v4.6.14
- Simplify implementation for intercept
v4.6.13
what changes?
- Support for temporarily replacing the interceptor list
for example:
/// Mock intercept that just prints SQL #[derive(Debug)] pub struct MockIntercept; #[async_trait] impl Intercept for MockIntercept { async fn before( &self, task_id: i64, _rb: &dyn Executor, sql: &mut String, _args: &mut Vec<Value>, _result: ResultType<&mut Result<ExecResult, rbatis::Error>, &mut Result<Vec<Value>, rbatis::Error>>, ) -> Result<Option<bool>, rbatis::Error> { *sql = sql.replace("<my_table_name>", &format!("activity_{}",task_id % 2)); println!("MockIntercept: SQL = {}", sql); Ok(Some(true)) } } #[tokio::main] pub async fn main() -> Result<(), rbatis::Error> { _ = fast_log::init(fast_log::Config::new().console()); let rb = RBatis::new(); rb.init(rbdc_sqlite::driver::SqliteDriver {}, "sqlite://target/sqlite.db")?; // create table _=rb.exec("CREATE TABLE activity_0 ( id INTEGER PRIMARY KEY);", vec![]).await; _=rb.exec("CREATE TABLE activity_1 ( id INTEGER PRIMARY KEY);", vec![]).await; let len = rb.intercepts.len(); println!("len={}", len); // Create new intercept list and add our mock intercept let new_intercept = Arc::new(SyncVec::new()); let intercept: Arc<dyn Intercept> = Arc::new(MockIntercept {}); new_intercept.push(intercept); // Create connection and replace its intercepts let mut conn = rb.acquire().await?; conn.intercepts = new_intercept; println!("conn.intercepts.len={}", conn.intercepts.len()); // Execute query to see the mock intercept in action let _ = conn.query("SELECT <my_table_name>", vec![]).await; let data = Activity::select_all(&conn).await?; println!("data={:?}", json!(data)); Ok(()) } /// table #[derive(serde::Serialize, serde::Deserialize, Clone)] pub struct Activity { 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<DateTime>, pub version: Option<i64>, pub delete_flag: Option<i32>, } //crud!(Activity {},"activity"); crud!(Activity {},"<my_table_name>");
v4.6.12
what changes?
- impl #591 for
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)] struct MockTable { pub id: Option<String>, pub name: Option<String>, pub status: Option<i32> } crud!(MockTable{}); let r = MockTable::update_by_map(rb, &table, value!{"id":"1", "column": ["name", "status"]}).await;
- impl
crud!for select target columns
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)] struct MockTable { pub id: Option<String>, pub name: Option<String>, pub status: Option<i32> } crud!(MockTable{}); let tables = MockTable::select_by_map(rb,value!{"id":"1", "column": ["id", "name"]}).await?;
v4.6.9
- this is an doc fix version
- merge #586
v4.6.8
add page method:
/// create Vec<PageRequest> from (total: u64, page_size: u64) pub fn make_page_requests(total: u64, page_size: u64) -> Vec<PageRequest> { let mut result = vec![]; let pages = PageRequest::new(1, page_size).set_total(total).pages(); for idx in 0..pages { let current_page = PageRequest::new(idx + 1, page_size).set_total(total); result.push(current_page); } result }