Documentation ¶
Index ¶
- Variables
- func BeginTransaction(ctx context.Context, db *sqlx.DB, opts *sql.TxOptions) (context.Context, error)
- func CommitTransaction(ctx context.Context) error
- func ErrIgnoreNoRows(err error) error
- func GetTransaction(ctx context.Context) (*sqlx.Tx, error)
- func LowerColumn(column string) string
- func MatchesAnyPrefixSuffix(value string) string
- func RollbackTransaction(ctx context.Context) error
- func RunInTransaction(ctx context.Context, db *sqlx.DB, opts *sql.TxOptions, f TxFunc) error
- type Access
- type Accessor
- type DB
- func (db DB) A(ctx context.Context) Access
- func (db DB) Begin(ctx context.Context) (context.Context, error)
- func (db DB) Commit(ctx context.Context) error
- func (db DB) GetAccess(ctx context.Context) Access
- func (db DB) Q(ctx context.Context) squirrel.StatementBuilderType
- func (db DB) QueryBuilder(ctx context.Context) squirrel.StatementBuilderType
- func (db DB) Rollback(ctx context.Context) error
- func (db DB) RunInSubTransaction(ctx context.Context, f TxFunc) error
- func (db DB) RunInTransaction(ctx context.Context, f TxFunc) error
- type TransactionRunner
- type Transactioner
- type TxFunc
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNoTransactionCtx is error when no transaction in context. ErrNoTransactionCtx = errors.New("no transaction found in context") // ErrTransactionAlreadyStarted is error when you try to start already started transaction, to start sub transaction, see RunInSubTransaction. ErrTransactionAlreadyStarted = errors.New("transaction is already started") )
Functions ¶
func BeginTransaction ¶
func BeginTransaction(ctx context.Context, db *sqlx.DB, opts *sql.TxOptions) (context.Context, error)
BeginTransaction from the context.
func CommitTransaction ¶
CommitTransaction from the context.
func GetTransaction ¶
GetTransaction will get transaction from context.
func LowerColumn ¶
LowerColumn returns PostgreSQL column name wrapped with LOWER function. Unsafe if using it manually in query.
func MatchesAnyPrefixSuffix ¶
MatchesAnyPrefixSuffix formats value wrapped with % % that will match any prefix or suffix. Unsafe if using it manually in query.
func RollbackTransaction ¶
RollbackTransaction from the context.
Types ¶
type Accessor ¶
type Accessor interface { sqlx.Queryer sqlx.Execer sqlx.ExtContext sqlx.Preparer sqlx.PreparerContext 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 NamedQuery(query string, arg interface{}) (*sqlx.Rows, error) NamedExec(query string, arg interface{}) (sql.Result, error) }
Accessor interface is common interface of sqlx.DB and sqlx.Tx types to access the sqlx full capabilities.
type DB ¶
DB will hold DB object.
func (DB) Q ¶
func (db DB) Q(ctx context.Context) squirrel.StatementBuilderType
Q is shorter method for QueryBuilder
func (DB) QueryBuilder ¶
func (db DB) QueryBuilder(ctx context.Context) squirrel.StatementBuilderType
QueryBuilder is PostgreSQL wrapper for squirrel. Chaining will not have side effect, so you have to use the returning value as a new builder.
Example:
repo := NewDB(db) id := "id" qb := repo.QueryBuilder(context.Background()).Select("*").From("table") // side effect free qb = qb.Where(`r.id = ?`, id) query, args, err := qb.ToSql() if err != nil { panic(err) } var result string if err := repo.QueryRowx(query, args...).Scan(&result); err != nil { panic(err) } fmt.Println(result)
func (DB) RunInSubTransaction ¶
RunInSubTransaction will spawn sub transaction.
type TransactionRunner ¶
TransactionRunner is transaction runner.
A transaction abstraction without exposing database or library implementation details and reducing cognitive load using closure.
This abstraction can be used to build deep modules.
See how:
- the repository hold data layer and service/core/business logic not knowing the concrete database implementation, pointer to db, or even specific library.
- the repository can be used inside or outside the transaction.
- changing repository function implementation will not affect repository signature.
Example:
type Repository interface { GetSegment(ctx context.Context, id int) (string, error) SaveRecipe(ctx context.Context, recipe string, segment string) error } type Publisher interface { Publish(ctx context.Context, eventType string, recipe ...string) error } type BusinessLogic struct { TransactionRunner repo Repository publisher Publisher } s := BusinessLogic{} id := 2 recipe := "recipe" err := s.RunInTransaction(context.Background(), func(ctx context.Context) error { segment, err := s.repo.GetSegment(ctx, id) if err != nil { return err } if err := s.repo.SaveRecipe(ctx, segment, recipe);err != nil { return err } if err := s.publisher.Publish(ctx, "created", recipe); err != nil { return err } return nil })
In the example:
If in the first iteration, we have repo.GetSegment making db calls, changing implementation using a hashmap (in-memory cache), or even using a different persistent layer technology, we don't need to change signatures or create other services.
Similar to publisher.Publish, the core logic doesn't care about the implementation of how publisher Publish events, with outbox workers or directly to Kafka the signature will be the same.
"The module should be deep." ~ John Ousterhout's A Philosophy of Software Design
type Transactioner ¶
type Transactioner interface { Begin(ctx context.Context) (context.Context, error) Rollback(ctx context.Context) error Commit(ctx context.Context) error }
Transactioner is abstraction of transaction without closure. with this abstraction the caller need to write transaction boilerplate in order to avoid closure.