暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Golang对PostgreSql事务处理

DB之路 2021-04-20
2335

于数据库执行多个更新操作时,事务会将多个操作当成单个单元处理,要成功都成功,否则都失败。本文通过实例说明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事务实现,并对事务相关操作进行封装,使用户只需关心业务操作。


文章转载自DB之路,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论