zino-rs / zino

Next-generation framework for composable applications in Rust.
https://crates.io/crates/zino
MIT License
762 stars 52 forks source link

数据库事务好像不生效 schema的设计 每个事务都是单独的 好像包裹不了事务管理 #112

Closed AdairStone closed 3 months ago

photino commented 3 months ago

事务处理有个独立的Transaction trait

AdairStone commented 3 months ago

事务处理有个独立的Transaction trait

let t = User::transaction(|trasaction| { Box::pin(async move { // let con = trasaction.acquire().await?; // let sql = user.insert_sql().await?; user.clone().insert().await?; let shop = Shops::from_domain(shops_domain.clone())?; shop.clone().insert().await?; for i in 1..3 { let mut invite_code = ShopsInviteCode::default(); invite_code.id = Uuid::now_v7(); invite_code.code = Self::generate_invite_code().await?; invite_code.discount = Some( Decimal::from_f64_retain(0.05f64 * (i as f64)) .unwrap_or(Decimal::default()), ); invite_code.shop_id = shop.id; invite_code.status = Some("active".to_string()); invite_code.label = Some(format!("Level{i}")); invite_code.insert().await?; } Ok(shop) }) 这种写法好像生效不了

AdairStone commented 3 months ago

这种写法好像生效不了 只能单独再执行sql 如果调用的schema里面的insert方法 里面的实现都是 单独建的新的connection执行环境 不在一个 transaction执行环境中 事务管理不了

AdairStone commented 3 months ago

https://github.com/launchbadge/sqlx/blob/main/examples/postgres/transaction/src/main.rs sqlx对应的事务执行时 跟connection 绑定才会生效

AdairStone commented 3 months ago

一般需要加事务的可能是跨表的复杂任务 单个表的事务管理操作本身就是原子的意义不是很大

photino commented 3 months ago

是的 如果是用这个transaction方法,里面的sql语句执行都要自己手动控制的,不能引用Schema上的方法

AdairStone commented 3 months ago

已经留了口子的话,感觉可以把schema对应的sql暴露出来方便复用 进行事务控制

photino commented 3 months ago

如果暴露出来进行事务控制的话 就得到处传递connection参数,对于使用者来说就有点麻烦了

AdairStone commented 3 months ago

把执行的sql暴露出来就行 通过transaction能够获取到connection 直接通过 connection 执行; 像下面这样 :transaction(|trasaction| { Box::pin(async move { let con = trasaction.acquire().await?; let sql = user.clone().insert_sql().await?; let r = con.execute(sql.as_str()).await?; if r.rows_affected() != 1 { return Err(warn!("user insert affected {}", r.rows_affected())); }} 不过项目中要额外引入sqlx_core Acquire的依赖

AdairStone commented 3 months ago

let t = User::transaction(|trasaction| { Box::pin(async move { let con = trasaction.acquire().await?; let sql = user.clone().insert_sql().await?; let r = con.execute(sql.as_str()).await?; if r.rows_affected() != 1 { return Err(warn!("user insert affected {}", r.rows_affected())); } shops_domain.user_id = Some(user.id().clone()); let shop = Shops::from_domain(shops_domain.clone())?; let shop_r = con.execute(shop.clone().insert_sql().await?.as_str()).await?; if shop_r.rows_affected() != 1 { return Err(warn!("shop insert affected {}", r.rows_affected())); } for i in 1..3 { let mut invite_code = ShopsInviteCode::default(); invite_code.id = Uuid::now_v7(); invite_code.code = Self::generate_invite_code().await?; invite_code.discount = Some( Decimal::from_f64_retain(0.05f64 * (i as f64)) .unwrap_or(Decimal::default()), ); invite_code.shop_id = shop.clone().id; invite_code.status = Some("active".to_string()); invite_code.label = Some(format!("Level{i}")); let invite_code_r = con .execute(invite_code.insert_sql().await?.as_str()) .await?; if invite_code_r.rows_affected() != 1 { return Err(warn!("shop invite_code affected {}", r.rows_affected())); } } Ok(shop) }) }) .await?; 像这样

photino commented 3 months ago

好想法!我看看以什么方式把这种sql暴露出来

如果你上面这个功能可以拆分成两个事务的话,目前倒是可以使用transactional_insertinsert_many来实现

AdairStone commented 3 months ago

这两个方法只能处理同一个schema 跨表好像还不行 现在的tablename是Self演变来的

photino commented 3 months ago

这两个方法只能处理同一个schema 跨表好像还不行 现在的tablename是Self演变来的

transactional_insert就是对插入两个不同的模型设计的,可以跨表的

AdairStone commented 3 months ago

image 这个地方可能有个bug tablename要根据S来

AdairStone commented 3 months ago

列好像也是的

photino commented 3 months ago

这个插入的是 self, 插入Vec<S>的还在后面,总共是插入1 + models.len()条数据

AdairStone commented 3 months ago

好吧 我还没用过这个功能;当时看了一眼感觉没满足我的需求,我就没细看了。 image 不过针对这个modles:Vec<S> 的参数的处理 S可能是不同的Schema 映射 ;应该要在一个循环里面处理对应Schema的表名 列名 以及值映射然后生成Sql 。 目前这样批量处理的话 好像S也只有一个相同的Schema了。不知道你的设计是否本身就是这样的。

photino commented 3 months ago

是的 主要是这个 Vec的类型得是相同的,所以只能处理插入两个模型的情形 不过我在考虑添加方法把生成的SQL直接暴露出来

AdairStone commented 3 months ago

是的 主要是这个 Vec的类型得是相同的,所以只能处理插入两个模型的情形 不过我在考虑添加方法把生成的SQL直接暴露出来

嗯 挺好的

photino commented 3 months ago

我们在Add prepare methods in Schema这个commit中引入了一系列的prepare_方法,返回QueryContext,可以通过它的.query方法直接拿到生成的SQL

AdairStone commented 3 months ago

能拿到sql 事务控制就方便多了 可以执行任意复杂事务操作

photino commented 3 months ago

是的 感谢提议 🤝

photino commented 3 months ago

zino-core 0.24.1已发布:https://docs.rs/zino-core/latest/zino_core/orm/trait.Schema.html