geektutu / blog

极客兔兔的博客,Coding Coding 创建有趣的开源项目。

Home Page:https://geektutu.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

动手写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更重要

@liangjfblue

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
}
commented

对mysql来说 测试回滚的例子是不是不对
mysql当执行到DDL语句时,会隐式的将当前回话的事务进行一次“COMMIT”操作

@cwww3
对mysql来说 测试回滚的例子是不是不对
mysql当执行到DDL语句时,会隐式的将当前回话的事务进行一次“COMMIT”操作

确实,测试中第7行CreateTable后回隐式commit导致无法全部回滚

@liangjfblue

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 出来的,生命周期都在这个方法里