动手写ORM框架 - GeeORM第六天 支持事务(Transaction) | 极客兔兔
geektutu opened this issue · comments
https://geektutu.com/post/geeorm-day6.html
7天用 Go语言/golang 从零实现 ORM 框架 GeeORM 教程(7 days implement golang object relational mapping framework from scratch tutorial),动手写 ORM 框架,参照 gorm, xorm 的实现。介绍数据库中的事务(transaction);封装事务,用户自定义回调函数实现原子操作。
事务这个操作六 我得赶紧把我项目改成这种
@xiaoxfan 事务参考了 stackoverflow 的实现,这个实现感觉蛮精巧的,代码很优雅。
hello 我想问下您rollback 的错误为什么忽略掉了呢
_ = s.Rollback()
谢谢!
func transactionRollback(t *testing.T) {
engine := OpenDB(t)
defer engine.Close()
s := engine.NewSession()
_ = s.Model(&User{}).DropTable()
_, err := engine.Transaction(func(s *session.Session) (result interface{}, err error) {
_ = s.Model(&User{}).CreateTable()
_, err = s.Insert(&User{"Tom", 18})
return nil, errors.New("Error")
})
if err == nil || s.HasTable() {
t.Fatal("failed to rollback")
}
}
这里 err == nil || s.HasTable() 的判断是不是错了? 自定义的errors.New("Error")确实让err == nil 不通过, 但是确实是创建了表, 所以 s.HasTable() 是true的, 从而导致单元测试失败了
@bxclib2
hello 我想问下您rollback 的错误为什么忽略掉了呢
_ = s.Rollback()
谢谢!
回滚不覆盖err, 是因为回滚就是因为有业务的报错, 所以不应该被这条语句覆盖掉业务的err, 业务err比回滚失败的err更重要
func transactionRollback(t *testing.T) { engine := OpenDB(t) defer engine.Close() s := engine.NewSession() _ = s.Model(&User{}).DropTable() _, err := engine.Transaction(func(s *session.Session) (result interface{}, err error) { _ = s.Model(&User{}).CreateTable() _, err = s.Insert(&User{"Tom", 18}) return nil, errors.New("Error") }) if err == nil || s.HasTable() { t.Fatal("failed to rollback") } }这里 err == nil || s.HasTable() 的判断是不是错了? 自定义的errors.New("Error")确实让err == nil 不通过, 但是确实是创建了表, 所以 s.HasTable() 是true的, 从而导致单元测试失败了
这里就是为了测试rollback的,先Droptable,然后在事务里创建了User表,如果最后有User表,说明rollback失败了
func (engine *Engine) Transaction(f TxFunc) (result interface{}, err error) {
s := engine.NewSession()
if err := s.Begin(); err != nil {
return nil, err
}
defer func() {
if p := recover(); p != nil {
_ = s.Rollback()
panic(p) // re-throw panic after Rollback
} else if err != nil {
_ = s.Rollback() // err is non-nil; don't change it
} else {
err = s.Commit() // err is nil; if Commit returns error update err
}
}()
return f(s)
}
请教一下err = s.Commit()
这里err不为nil时是否还需要Rollback?我看gorm把这个err也加进了需要Rollback的判断里
@furthergo 感谢指出,Commit 失败再回滚一次,应该是需要的。参考 gorm,改成这样子会安全一些:
} else {
defer func () {
if err != nil {
_ = s.Rollback()
}
}
err = s.Commit() // err is nil; if Commit returns error update err
}
对mysql来说 测试回滚的例子是不是不对
mysql当执行到DDL语句时,会隐式的将当前回话的事务进行一次“COMMIT”操作
@cwww3
对mysql来说 测试回滚的例子是不是不对
mysql当执行到DDL语句时,会隐式的将当前回话的事务进行一次“COMMIT”操作
确实,测试中第7行CreateTable后回隐式commit导致无法全部回滚
func transactionRollback(t *testing.T) { engine := OpenDB(t) defer engine.Close() s := engine.NewSession() _ = s.Model(&User{}).DropTable() _, err := engine.Transaction(func(s *session.Session) (result interface{}, err error) { _ = s.Model(&User{}).CreateTable() _, err = s.Insert(&User{"Tom", 18}) return nil, errors.New("Error") }) if err == nil || s.HasTable() { t.Fatal("failed to rollback") } }这里 err == nil || s.HasTable() 的判断是不是错了? 自定义的errors.New("Error")确实让err == nil 不通过, 但是确实是创建了表, 所以 s.HasTable() 是true的, 从而导致单元测试失败了
这里就是为了测试rollback的,先Droptable,然后在事务里创建了User表,如果最后有User表,说明rollback失败了
是的, 感谢指出
是不是在执行完成Commit和Rollback方法最后中要将s.tx设置为nil呢,否则一旦开启事务后。之后所有的sql语句都将通过s.tx来执行了
@bowenddd
是不是在执行完成Commit和Rollback方法最后中要将s.tx设置为nil呢,否则一旦开启事务后。之后所有的sql语句都将通过s.tx来执行了
不用啊,Transaction
里的 session 在方法中 New 出来的,生命周期都在这个方法里