## 执行数据库事务接口 在处理数据库连接服务时,测试相对困难。原因在于Go的鸭子模型导致在运行时模拟很不方便。我建议在使用数据库时使用存储接口,使用这样的方式模拟数据库事务接口同样可行,在后续章节会讨论这一点。本节将着眼于对数据库事务的操作。 我们将改写上一节的create和query文件,最终的输出很类似,但是create和query操作将包含在事务中。 ### 实践 1. 建立 transaction.go: ``` package dbinterface import "database/sql" // DB是sql.DB或sql.Transaction满足的接口 type DB interface { Exec(query string, args ...interface{}) (sql.Result, error) Prepare(query string) (*sql.Stmt, error) Query(query string, args ...interface{}) (*sql.Rows, error) QueryRow(query string, args ...interface{}) *sql.Row } // Transaction可以执行任何 Query, Commit, Rollback, 和 Stmt 操作 type Transaction interface { DB Commit() error Rollback() error Stmt(stmt *sql.Stmt) *sql.Stmt } ``` 2. 建立 create.go: ``` package dbinterface import _ "github.com/go-sql-driver/mysql" // Create建立example表并填充数据 func Create(db DB) error { // create the database if _, err := db.Exec("CREATE TABLE example (name VARCHAR(20), created DATETIME)"); err != nil { return err } if _, err := db.Exec(`INSERT INTO example (name, created) values ("Aaron", NOW())`); err != nil { return err } return nil } ``` 3. 建立 exec.go: ``` package dbinterface func Exec(db DB) error { // 依然不推荐这么写 因为没有处理所返回的可能存在的错误 defer db.Exec("DROP TABLE example") if err := Create(db); err != nil { return err } if err := Query(db); err != nil { return err } return nil } ``` 4. 建立 main.go: ``` package main import ( "github.com/agtorre/go-cookbook/chapter5/database" "github.com/agtorre/go-cookbook/chapter5/dbinterface" _ "github.com/go-sql-driver/mysql" ) func main() { db, err := database.Setup() if err != nil { panic(err) } tx, err := db.Begin() if err != nil { panic(err) } defer tx.Rollback() if err := dbinterface.Exec(db); err != nil { panic(err) } if err := tx.Commit(); err != nil { panic(err) } } ``` 5. 这会输出: ``` Results: Name: Aaron Created: 2017-02-16 20:00:00 +0000 UTC ``` ### 说明 本节与上一节以非常相似的方式工作,演示了使用事务并生成适用于sql.DB连接和sql.Transaction对象的通用数据库函数。我们会在第八章看到对这些模拟接口的测试使用。 * * * * 学识浅薄,错误在所难免。欢迎在群中就本书提出修改意见,以飨后来者,长风拜谢。 Golang中国(211938256) beego实战(258969317) Go实践(386056972)