diff --git a/.example/database/gdb/mysql/gdb_transaction.go b/.example/database/gdb/mysql/gdb_transaction.go new file mode 100644 index 000000000..fe5642ca2 --- /dev/null +++ b/.example/database/gdb/mysql/gdb_transaction.go @@ -0,0 +1,27 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" +) + +func main() { + var ( + db = g.DB() + table = "user" + ) + tx, err := db.Begin() + if err != nil { + panic(err) + } + if err = tx.Begin(); err != nil { + panic(err) + } + _, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert() + if err = tx.Rollback(); err != nil { + panic(err) + } + _, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert() + if err = tx.Commit(); err != nil { + panic(err) + } +} diff --git a/.example/database/gdb/mysql/gdb_transaction_closure.go b/.example/database/gdb/mysql/gdb_transaction_closure.go new file mode 100644 index 000000000..aa05d48d4 --- /dev/null +++ b/.example/database/gdb/mysql/gdb_transaction_closure.go @@ -0,0 +1,34 @@ +package main + +import ( + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/frame/g" +) + +func main() { + var ( + err error + db = g.DB() + table = "user" + ) + if err = db.Transaction(func(tx *gdb.TX) error { + // Nested transaction 1. + if err = tx.Transaction(func(tx *gdb.TX) error { + _, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert() + return err + }); err != nil { + return err + } + // Nested transaction 2, panic. + if err = tx.Transaction(func(tx *gdb.TX) error { + _, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert() + // Create a panic that can make this transaction rollback automatically. + panic("error") + }); err != nil { + return err + } + return nil + }); err != nil { + panic(err) + } +} diff --git a/.example/database/gdb/mysql/gdb_transaction_savepoint.go b/.example/database/gdb/mysql/gdb_transaction_savepoint.go new file mode 100644 index 000000000..e677f15ab --- /dev/null +++ b/.example/database/gdb/mysql/gdb_transaction_savepoint.go @@ -0,0 +1,40 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" +) + +func main() { + var ( + err error + db = g.DB() + table = "user" + ) + tx, err := db.Begin() + if err != nil { + panic(err) + } + defer func() { + if err := recover(); err != nil { + _ = tx.Rollback() + } + }() + if _, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert(); err != nil { + panic(err) + } + if err = tx.SavePoint("MyPoint"); err != nil { + panic(err) + } + if _, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert(); err != nil { + panic(err) + } + if _, err = tx.Model(table).Data(g.Map{"id": 3, "name": "green"}).Insert(); err != nil { + panic(err) + } + if err = tx.RollbackTo("MyPoint"); err != nil { + panic(err) + } + if err = tx.Commit(); err != nil { + panic(err) + } +} diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index b9a448b7d..be69262a6 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -32,11 +32,11 @@ type DB interface { // Model creation. // =========================================================================== + // Table function is deprecated, use Model instead. // The DB interface is designed not only for // relational databases but also for NoSQL databases in the future. The name // "Table" is not proper for that purpose any more. - // Also see Core.Table. - // Deprecated, use Model instead. + // Also see Core.Table. Table(table ...string) *Model // Model creates and returns a new ORM model from given schema. @@ -191,6 +191,8 @@ type DB interface { mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) // See Core.mappingAndFilterData. convertFieldValueToLocalValue(fieldValue interface{}, fieldType string) interface{} // See Core.convertFieldValueToLocalValue. convertRowsToResult(rows *sql.Rows) (Result, error) // See Core.convertRowsToResult. + addSqlToTracing(ctx context.Context, sql *Sql) // See Core.addSqlToTracing. + writeSqlToLogger(v *Sql) // See Core.writeSqlToLogger. } // Core is the base struct for database management. diff --git a/database/gdb/gdb_transaction.go b/database/gdb/gdb_transaction.go index 7f71a3662..63031b815 100644 --- a/database/gdb/gdb_transaction.go +++ b/database/gdb/gdb_transaction.go @@ -9,6 +9,7 @@ package gdb import ( "database/sql" "fmt" + "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gconv" "reflect" @@ -36,7 +37,27 @@ func (tx *TX) Commit() error { _, err := tx.Exec("RELEASE SAVEPOINT " + tx.transactionKey()) return err } - return tx.tx.Commit() + var ( + sqlStr = "COMMIT" + mTime1 = gtime.TimestampMilli() + err = tx.tx.Commit() + mTime2 = gtime.TimestampMilli() + sqlObj = &Sql{ + Sql: sqlStr, + Type: "TX.Commit", + Args: nil, + Format: sqlStr, + Error: err, + Start: mTime1, + End: mTime2, + Group: tx.db.GetGroup(), + } + ) + tx.db.addSqlToTracing(tx.db.GetCtx(), sqlObj) + if tx.db.GetDebug() { + tx.db.writeSqlToLogger(sqlObj) + } + return err } // Rollback aborts current transaction. @@ -48,7 +69,27 @@ func (tx *TX) Rollback() error { _, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.transactionKey()) return err } - return tx.tx.Rollback() + var ( + sqlStr = "ROLLBACK" + mTime1 = gtime.TimestampMilli() + err = tx.tx.Rollback() + mTime2 = gtime.TimestampMilli() + sqlObj = &Sql{ + Sql: sqlStr, + Type: "TX.Rollback", + Args: nil, + Format: sqlStr, + Error: err, + Start: mTime1, + End: mTime2, + Group: tx.db.GetGroup(), + } + ) + tx.db.addSqlToTracing(tx.db.GetCtx(), sqlObj) + if tx.db.GetDebug() { + tx.db.writeSqlToLogger(sqlObj) + } + return err } // Begin starts a nested transaction procedure.