Golang PostgreSql数据库事务处理

对于数据库执行多个更新操作时,事务会将多个操作当成单个单元处理,要成功都成功,否则都失败。本文通过实例说明Go Sql事务处理。

1. 环境准备

​ 搭建PostgreSql数据库环境,我们打算在postgre数据库中新建表meta_data(id, source),然后插入记录测试事务。

​ go操作数据库的包为"database/sql",这里数据库使用postgreSql,需要导入github.com/lib/pq;下面是准备数据库连接代码。

import ( 
   "database/sql" 
   "fmt" 
   _ "github.com/lib/pq" 
   uuid "github.com/satori/go.uuid" 
   "log" 
   "strings" 
) 
 
const ( 
   host     = "192.168.0.111" 
   port     = 5432 
   user     = "yourDbUser" 
   password = "yourDbPassword" 
   dbname   = "postgres" 
) 
 
var db *sql.DB 
 
func init() {
    
   var err error 
   connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname) 
   db, err = sql.Open("postgres", connStr) 
   if err != nil {
    
      log.Fatal(err) 
   } 
   db.SetConnMaxLifetime(500) 
   db.SetMaxIdleConns(50) 
   db.SetMaxOpenConns(10) 
   db.Stats() 
} 
 
func CheckError(err error) {
    
   if err != nil {
    
      panic(err) 
   } 
} 

go默认实现了数据库连接池,无需引入第三方连接池实现。

2. 事务实现

下面是事务示例实现,我们使用postgreSql,因此变量占位符使用 $ , MySql使用 ?。

func InsertData() {
    
	data := [] string{
   "DGraph","Redis"} 
	sql := "INSERT INTO meta_data(id, source) VALUES($1, $2)" 
	tx,err := db.Begin() 
	CheckError(err) 
	defer tx.Commit() 
 
	for _, item := range data {
    
		sid := strings.Replace(uuid.NewV4().String(),"-","",-1) 
		_, err = db.Exec(sql, sid, item) 
 
		if err != nil {
    
			_ = tx.Rollback() 
            break 
		} 
	} 
} 

函数开始之前首先启动事务,使用defer语句确保最后提交事务。接着执行多个插入语句,如果有错误回滚事务。

这个实现没有问题,只是每次需要写和事务相关的代码,显得多余,最好能封装事务相关代码,用户只关心业务。

3. 事务操作封装

上节手动实现了事务,本节对事务操作进行封装,让代码更有通用性。

这里定义UpdateWithTx()函数,其中封装了事务相关代码,具体执行和数据库相关操作通过其函数参数传入。

func UpdateWithTx(fn func(tx *sql.Tx) error) error {
    
   var tx,err = db.Begin() 
   if err != nil{
    
      return err 
   } 
 
   defer func() {
    
      switch err {
    
      case nil: 
         err = tx.Commit() 
      default: 
         tx.Rollback() 
      } 
   }() 
 
   err = fn(tx) 
 
   return err 
} 

首先获得事务指针,接着写匿名函数,使用defer关键词确保函数执行完成之前自动管控事务。最后执行实际操作的函数并返回状态。下面我们测试该函数。

func TestUpdateWithTx(t *testing.T) {
    
   UpdateWithTx(func(tx *sql.Tx) error {
    
      var err error 
      data := [] string{
   "DGraph1","Redis1","MongoDb","BoltDB"} 
      sql := "INSERT INTO meta_data(id, source) VALUES($1, $2)" 
 
      for _, item := range data {
    
         sid := strings.Replace(uuid.NewV4().String(),"-","",-1) 
         if _, err = tx.Exec(sql, sid, item); err != nil{
    
            break 
         } 
         // err = errors.New("人为错误") 
      } 
      return err 
   }) 
} 

具体业务和上节代码一样,但我们不在关心事务相关代码,用户可测试有错误时是否回滚事务。

4. 总结

本文通过示例介绍了Go Sql事务实现,并对事务相关操作进行封装,使用户只需关心业务操作。


本文参考链接:https://blog.csdn.net/neweastsun/article/details/107817240