Skip to content

Instantly share code, notes, and snippets.

@byrnedo
Last active December 19, 2023 12:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save byrnedo/b60c6f08bf74576f09fadeac7773c3d8 to your computer and use it in GitHub Desktop.
Save byrnedo/b60c6f08bf74576f09fadeac7773c3d8 to your computer and use it in GitHub Desktop.
Repo setup for go that lets you use transactions transparently
package repo
import (
"context"
"database/sql/driver"
"github.com/jmoiron/sqlx"
)
// can be either a *sqlx.DB or a *sqlx.Tx
// should have methods that are common to both
type executor interface {
sqlx.ExtContext
SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error
GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error
}
type Repo interface {
Begin(ctx context.Context) (Repo, error)
// has Commit and Rollback sigs
driver.Tx
// TODD ADD YOUR OTHER METHODS HERE
InsertFoo(context.Context, *Foo) error
}
// Our implementation
type SqlRepo struct {
DB *sqlx.DB
// if this is a normal instance it'll be *sql.DB, otherwise a *sqlx.Tx
executor
}
func (r SqlRepo) Begin(ctx context.Context) (Repo, error) {
exr, err := r.DB.BeginTxx(ctx, &sql.TxOptions{})
if err != nil {
return nil, err
}
return SqlRepo{DB: r.DB, executor: exr}, nil
}
func (r SqlRepo) Commit() (err error) {
if e, ok := r.executor.(*sqlx.Tx); ok {
return e.Commit()
}
return errors.New("not in a transaction")
}
func (r SqlRepo) Rollback() error {
if e, ok := r.executor.(*sqlx.Tx); ok {
return e.Rollback()
}
return errors.New("not in a transaction")
}
type Foo struct {
ID string
Name string
CreatedAt time.Time `db:"created_at"`
}
func (r SqlRepo) InsertFoo(ctx context.Context, thing *Foo) error {
return r.executor.GetContext(ctx, thing, `INSERT INTO foo (name) VALUES ($1)
RETURNING id, created_at
`, thing.Name)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment