Documentation
¶
Overview ¶
Package sqlx is an implementation of trm.Transaction interface by Transaction for sqlx.Tx.
Example ¶
Example demonstrates the implementation of the Repository pattern by trm.Manager.
package main
import (
"context"
"fmt"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
trmsqlx "github.com/avito-tech/go-transaction-manager/sqlx"
"github.com/avito-tech/go-transaction-manager/trm/manager"
)
// Example demonstrates the implementation of the Repository pattern by trm.Manager.
func main() {
db := newDB()
defer db.Close() //nolint:errcheck
sqlStmt := `CREATE TABLE IF NOT EXISTS user (user_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, username TEXT);`
_, err := db.Exec(sqlStmt)
checkErr(err, sqlStmt)
r := newRepo(db, trmsqlx.DefaultCtxGetter)
u := &user{
Username: "username",
}
ctx := context.Background()
trManager := manager.Must(trmsqlx.NewDefaultFactory(db))
err = trManager.Do(ctx, func(ctx context.Context) error {
if err := r.Save(ctx, u); err != nil {
return err
}
return trManager.Do(ctx, func(ctx context.Context) error {
u.Username = "new_username"
return r.Save(ctx, u)
})
})
checkErr(err)
userFromDB, err := r.GetByID(ctx, u.ID)
checkErr(err)
fmt.Println(userFromDB)
}
func newDB() *sqlx.DB {
db, err := sqlx.Open("sqlite3", "file:test?mode=memory")
checkErr(err)
return db
}
type repo struct {
db *sqlx.DB
getter *trmsqlx.CtxGetter
}
func newRepo(db *sqlx.DB, c *trmsqlx.CtxGetter) *repo {
return &repo{
db: db,
getter: c,
}
}
type user struct {
ID int64
Username string
}
type userRow struct {
ID int64 `db:"user_id"`
Username string `db:"username"`
}
func (r *repo) GetByID(ctx context.Context, id int64) (*user, error) {
query := "SELECT * FROM user WHERE user_id = ?;"
row := userRow{}
err := r.getter.DefaultTrOrDB(ctx, r.db).GetContext(ctx, &row, r.db.Rebind(query), id)
if err != nil {
return nil, err
}
return r.toModel(row), nil
}
func (r *repo) Save(ctx context.Context, u *user) error {
isNew := u.ID == 0
query := `UPDATE user SET username = :username WHERE user_id = :user_id;`
if isNew {
query = `INSERT INTO user (username) VALUES (:username);`
}
res, err := sqlx.NamedExecContext(
ctx,
r.getter.DefaultTrOrDB(ctx, r.db),
r.db.Rebind(query),
r.toRow(u),
)
if err != nil {
return err
} else if !isNew {
return nil
} else if u.ID, err = res.LastInsertId(); err != nil {
return err
}
// For PostgreSql need to use NamedQueryContext with RETURNING
// DO UPDATE SET username = EXCLUDED.username RETURNING id;
// defer res.Next()
// if u.ID == 0 && res.Next() {
// if err = res.Scan(&u.ID); err != nil {
// return err
// }
// }
return nil
}
func (r *repo) toRow(model *user) userRow {
return userRow{
ID: model.ID,
Username: model.Username,
}
}
func (r *repo) toModel(row userRow) *user {
return &user{
ID: row.ID,
Username: row.Username,
}
}
func checkErr(err error, args ...interface{}) {
if err != nil {
panic(fmt.Sprint(append([]interface{}{err}, args...)...))
}
}
Output: &{1 new_username}
Example (Chained) ¶
Example demonstrates a work of manager.ChainedMW.
// connect DB
db1 := newDB()
defer db1.Close() //nolint:errcheck
db2 := newDB()
defer db2.Close() //nolint:errcheck
// create DB
sqlStmt := `CREATE TABLE IF NOT EXISTS user (user_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, username TEXT);`
_, err := db1.Exec(sqlStmt)
checkErr(err, sqlStmt)
_, err = db2.Exec(sqlStmt)
checkErr(err, sqlStmt)
// init manager
ctxKey1 := trmcontext.Generate()
m1 := manager.Must(
trmsqlx.NewDefaultFactory(db1),
manager.WithSettings(settings.Must(settings.WithCtxKey(ctxKey1))),
)
r1 := newRepo(db1, trmsqlx.NewCtxGetter(trmcontext.New(ctxKey1)))
ctxKey2 := trmcontext.Generate()
m2 := manager.Must(
trmsqlx.NewDefaultFactory(db2),
manager.WithSettings(settings.Must(settings.WithCtxKey(ctxKey2))),
)
r2 := newRepo(db2, trmsqlx.NewCtxGetter(trmcontext.New(ctxKey2)))
chainedManager := manager.MustChained([]trm.Manager{m1, m2})
u := &user{Username: "username"}
ctx := context.Background()
err = chainedManager.Do(ctx, func(ctx context.Context) error {
if err := r1.Save(ctx, u); err != nil {
return err
}
if err := r2.Save(ctx, u); err != nil {
return err
}
return chainedManager.Do(ctx, func(ctx context.Context) error {
u.Username = "new_username"
if err = r1.Save(ctx, u); err != nil {
return err
}
return r2.Save(ctx, u)
})
})
checkErr(err)
userFromDB1, err := r1.GetByID(ctx, u.ID)
checkErr(err)
userFromDB2, err := r1.GetByID(ctx, u.ID)
checkErr(err)
fmt.Println(userFromDB1, userFromDB2)
Output: &{1 new_username} &{1 new_username}
Index ¶
- Variables
- func NewDefaultFactory(db *sqlx.DB) trm.TrFactory
- func NewFactory(db *sqlx.DB, sp trmsql.SavePoint) trm.TrFactory
- type CtxGetter
- type Tr
- type Transaction
- func (t *Transaction) Begin(ctx context.Context, _ trm.Settings) (context.Context, trm.Transaction, error)
- func (t *Transaction) Closed() <-chan struct{}
- func (t *Transaction) Commit(ctx context.Context) error
- func (t *Transaction) IsActive() bool
- func (t *Transaction) Rollback(ctx context.Context) error
- func (t *Transaction) Transaction() interface{}
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var DefaultCtxGetter = NewCtxGetter(trmcontext.DefaultManager)
DefaultCtxGetter is the CtxGetter with settings.DefaultCtxKey.
Functions ¶
func NewDefaultFactory ¶ added in v1.0.0
NewDefaultFactory creates default trm.Transaction(sqlx.Tx).
Types ¶
type CtxGetter ¶ added in v1.0.0
type CtxGetter struct {
// contains filtered or unexported fields
}
CtxGetter gets Tr from trm.CtxManager by casting trm.Transaction to Tr.
func NewCtxGetter ¶ added in v1.0.0
func NewCtxGetter(c trm.CtxManager) *CtxGetter
NewCtxGetter returns *CtxGetter to get Tr from context.Context.
func (*CtxGetter) DefaultTrOrDB ¶ added in v1.0.0
DefaultTrOrDB returns Tr from context.Context or DB(Tr) otherwise.
type Tr ¶
type Tr interface {
sqlx.ExtContext
sqlx.Preparer
Preparex(query string) (*sqlx.Stmt, error)
PreparexContext(ctx context.Context, query string) (*sqlx.Stmt, error)
PrepareNamed(query string) (*sqlx.NamedStmt, error)
PrepareNamedContext(ctx context.Context, query string) (*sqlx.NamedStmt, error)
sqlx.Execer
MustExec(query string, args ...interface{}) sql.Result
MustExecContext(ctx context.Context, query string, args ...interface{}) sql.Result
NamedExec(query string, arg interface{}) (sql.Result, error)
NamedExecContext(ctx context.Context, query string, arg interface{}) (sql.Result, error)
sqlx.Queryer
QueryRow(query string, args ...interface{}) *sql.Row
QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
NamedQuery(query string, arg interface{}) (*sqlx.Rows, error)
Select(dest interface{}, query string, args ...interface{}) error
SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error
Get(dest interface{}, query string, args ...interface{}) error
GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error
}
Tr is an interface to work with sqlx.DB or sqlx.Tx. Stmtx, StmtxContext, NamedStmt and NamedStmtContext are not implemented!
type Transaction ¶ added in v1.0.0
type Transaction struct {
// contains filtered or unexported fields
}
Transaction is trm.Transaction for sqlx.Tx.
func NewTransaction ¶ added in v1.0.0
func NewTransaction( ctx context.Context, sp trmsql.SavePoint, opts *sql.TxOptions, db *sqlx.DB, ) (context.Context, *Transaction, error)
NewTransaction creates trm.Transaction for sqlx.Tx.
func (*Transaction) Begin ¶ added in v1.0.0
func (t *Transaction) Begin(ctx context.Context, _ trm.Settings) (context.Context, trm.Transaction, error)
Begin nested transaction by save point.
func (*Transaction) Closed ¶ added in v1.5.0
func (t *Transaction) Closed() <-chan struct{}
Closed returns a channel that's closed when transaction committed or rolled back.
func (*Transaction) Commit ¶ added in v1.0.0
func (t *Transaction) Commit(ctx context.Context) error
Commit closes the trm.Transaction.
func (*Transaction) IsActive ¶ added in v1.0.0
func (t *Transaction) IsActive() bool
IsActive returns true if the transaction started but not committed or rolled back.
func (*Transaction) Rollback ¶ added in v1.0.0
func (t *Transaction) Rollback(ctx context.Context) error
Rollback the trm.Transaction.
func (*Transaction) Transaction ¶ added in v1.0.0
func (t *Transaction) Transaction() interface{}
Transaction returns the real transaction sqlx.Tx. trm.NestedTrFactory returns IsActive as true while trm.Transaction is opened.