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