小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
事务
事务操作是通过三个方法实现:
- Begin():开启事务
- Commit():提交事务(执行sql)
- Rollback():回滚
在创建一个表,并插入两条数据:
CREATE TABLE `account` (
`id` int(4) NOT NULL,
`name` varchar(30) DEFAULT NULL,
`money` float(8,2) DEFAULT NULL,
PRIMARY KEY (`id`)
)
INSERT INTO `account` VALUES ('1', 'aa', '3000.00');
INSERT INTO `account` VALUES ('2', 'bb', '1000.00');
现在我们想让aa转账给bb2000元。
如果转账成功,修改aa和bb的金额。否则aa和bb的金额保持不变。
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main() {
/*
事务:
4大特性:ACID
原子性:
一致性:
隔离性:
永久性:
*/
//aa-->bb,2000元
db, _ := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/my1802?charset=utf8")
//开启事务
tx, _ := db.Begin()
//提供一组sql操作
var aff1, aff2 int64 = 0, 0
result1, _ := tx.Exec("UPDATE account SET money=3000 WHERE id=?", 1)
result2, _ := tx.Exec("UPDATE account SET money=2000 WHERE id=?", 2)
//fmt.Println(result2)
if result1 != nil {
aff1, _ = result1.RowsAffected()
}
if result2 != nil {
aff2, _ = result2.RowsAffected();
}
fmt.Println(aff1)
fmt.Println(aff2)
if aff1 == 1 && aff2 == 1 {
//提交事务
tx.Commit()
fmt.Println("操作成功。。")
} else {
//回滚
tx.Rollback()
fmt.Println("操作失败。。。回滚。。")
}
}
由于事务是一个一直连接的状态,所以Tx对象必须绑定和控制单个连接。一个Tx会在整个生命周期中保存一个连接,然后在调用commit()或Rollback()的时候释放掉。在调用这几个函数的时候必须十分小心,否则连接会一直被占用直到被垃圾回收。
使用事务需要注意事务的连接生命周期从Beigin函数调用起,直到Commit和Rollback函数的调用结束。事务也提供了prepare语句的使用方式,但是需要使用Tx.Stmt方法创建。prepare设计的初衷是多次执行,对于事务,有可能需要多次执行同一个sql。然而无论是正常的prepare和事务处理,prepare对于连接的管理都有点小复杂。因此私以为尽量避免在事务中使用prepare方式。