txhelper

package module
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 31, 2021 License: MIT Imports: 6 Imported by: 0

README

go-txhelper Actions Status Coverage Status PkgGoDev GoDoc go-report

go-txhelper is a module that abstracts database transaction behaviour. It does so by differentiating between Read and Write operations.

It is designed for the use of mappers where you do not know whether you need a transaction or not.

Example

package main

import (
	"context"
	"errors"
	"fmt"
	"os"

	"github.com/jackc/pgx/v4"
	"github.com/jackc/pgx/v4/pgxpool"

	"github.com/talon-one/go-txhelper"
)

func main() {
	ctx := context.Background()

	pool, err := pgxpool.Connect(ctx, os.Getenv("DATABASE_URL"))
	if err != nil {
		panic(err)
	}
	defer pool.Close()

	// create a users table
	_, err = pool.Exec(ctx, `CREATE TABLE IF NOT EXISTS users (
		id INTEGER PRIMARY KEY,
		name TEXT NOT NULL
	)`)
	if err != nil {
		panic(err)
	}

	um, err := NewUserMapper(pool)
	if err != nil {
		panic(err)
	}
	// we need to close the mapper at the end of our business logic
	defer um.Close(ctx)

	id := 1
	wantedName := "Joe"

	// Get the UserName of the specified user
	currentName, err := um.GetUserName(ctx, id)
	if err != nil {
		if !errors.Is(err, pgx.ErrNoRows) {
			panic(err)
		}
		// if there are no rows for this id create a user
		if err := um.CreateUser(ctx, id, wantedName); err != nil {
			panic(err)
		}
		fmt.Printf("Created User (id=%d, name=%s)\n", id, wantedName)
		return
	}
	if currentName != wantedName {
		if err := um.UpdateUserName(ctx, id, wantedName); err != nil {
			panic(err)
		}
		fmt.Printf("Updated User (id=%d, name=%s)\n", id, wantedName)
		return
	}
	fmt.Printf("nothing to do (id=%d, name=%s)\n", id, currentName)
}

type UserMapper struct {
	txh *txhelper.TxHelper
}

func NewUserMapper(pool *pgxpool.Pool) (*UserMapper, error) {
	txh, err := txhelper.New(pool, nil)
	if err != nil {
		return nil, err
	}
	return &UserMapper{
		txh: txh,
	}, nil
}

func (um *UserMapper) CreateUser(ctx context.Context, id int, name string) error {
	executor, err := um.txh.GetExecutor(ctx, txhelper.InsertOperation)
	if err != nil {
		return err
	}

	_, err = executor.Exec(ctx, "INSERT INTO users VALUES ($1, $2)", id, name)
	return err
}

func (um *UserMapper) GetUserName(ctx context.Context, id int) (string, error) {
	executor, err := um.txh.GetExecutor(ctx, txhelper.ReadOperation)
	if err != nil {
		return "", err
	}

	row := executor.QueryRow(ctx, "SELECT name FROM users WHERE id = $1", id)
	var name string
	if err := row.Scan(&name); err != nil {
		return "", err
	}

	return name, nil
}

func (um *UserMapper) UpdateUserName(ctx context.Context, id int, newName string) error {
	executor, err := um.txh.GetExecutor(ctx, txhelper.UpdateOperation)
	if err != nil {
		return err
	}

	_, err = executor.Exec(ctx, "UPDATE users SET name = $1 WHERE id = $2", newName, id)
	return err
}

func (um *UserMapper) Close(ctx context.Context) error {
	return um.txh.Commit(ctx)
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Executor

type Executor interface {
	Exec(ctx context.Context, sql string, arguments ...interface{}) (commandTag pgconn.CommandTag, err error)
	Query(ctx context.Context, sql string, args ...interface{}) (pgx.Rows, error)
	QueryRow(ctx context.Context, sql string, args ...interface{}) pgx.Row
}

Executor is a type that implements the Exec, Query and QueryRow of pgx, it can be used to hide the types pgxpool.Pool, pgx.Conn,...

type Hooks added in v0.0.2

type Hooks struct {
	BeforeStartTransaction func(*TxHelper, *pgxpool.Pool, context.Context) error
	AfterStartTransaction  func(*TxHelper, *pgxpool.Pool, pgx.Tx, context.Context) error
	BeforeCommit           func(*TxHelper, *pgxpool.Pool, pgx.Tx, context.Context) error
	AfterCommit            func(*TxHelper, *pgxpool.Pool, pgx.Tx, context.Context) error
	BeforeRollback         func(*TxHelper, *pgxpool.Pool, pgx.Tx, context.Context) error
	AfterRollback          func(*TxHelper, *pgxpool.Pool, pgx.Tx, context.Context) error
}

Hooks can be used to perform custom logic before or after certain events.

type Operation

type Operation int

Operation defines the operation that will be performed on the database.

const (
	// ReadOperation should be used for read only activities, such as selecting data.
	ReadOperation Operation = 1 << iota
	// InsertOperation should be used for the INSERT operation.
	InsertOperation
	// UpdateOperation should be used for the UPDATE operation.
	UpdateOperation
	// DeleteOperation should be used for the DELETE operation.
	DeleteOperation
)

func (Operation) Has

func (flag Operation) Has(b Operation) bool

Has checks if the Operation flag has the specified Operation.

type Options

type Options struct {
	Hooks Hooks
}

Options can be used to finetune txhelper.

type TxHelper

type TxHelper struct {
	// contains filtered or unexported fields
}

TxHelper is a helper that can be used inside mappers. Its purpose is to assist the mappers with transaction handling.

func New

func New(pool *pgxpool.Pool, options *Options) (*TxHelper, error)

New creates a new TxHelper that can be used inside mappers. Its purpose is to assist the mappers with transaction handling.

func (*TxHelper) Commit

func (tx *TxHelper) Commit(ctx context.Context) error

Commit commits the transaction (if there is one).

func (*TxHelper) GetExecutor

func (tx *TxHelper) GetExecutor(ctx context.Context, operations Operation) (Executor, error)

GetExecutor gets the executor for the specified operation. The executor can be used to query the database. GetExecutor will start and return a transaction if there is no existing transaction AND if the operation is either a InsertOperation, UpdateOperation or DeleteOperation. In case of a non existing transaction and a ReadOperation GetExecutor will NOT start a transaction.

func (*TxHelper) Rollback

func (tx *TxHelper) Rollback(ctx context.Context) error

Rollback rollbacks the transaction (if there is one), remember that you only need to Rollback if there was no error before.

Directories

Path Synopsis
gen
readme
read README.md.tmpl and find all “`go “` snippets and run them
read README.md.tmpl and find all “`go “` snippets and run them

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL