Skip to content

Latest commit

 

History

History
124 lines (103 loc) · 3.36 KB

README.md

File metadata and controls

124 lines (103 loc) · 3.36 KB

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)
	}

	txh, err := txhelper.New(pool, nil)
	if err != nil {
		panic(err)
	}
	// we need to close the helper at the end of our business logic
	defer func() {
		if err := txh.Commit(ctx); err != nil {
			panic(err)
		}
	}()

	um := NewUserMapper(txh)

	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(txh *txhelper.TxHelper) *UserMapper {
	return &UserMapper{
		txh: txh,
	}
}

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
}