pg

package module
v1.0.56 Latest Latest
Warning

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

Go to latest
Published: Apr 16, 2025 License: MPL-2.0 Imports: 6 Imported by: 0

README

Golang module for CRUD database operations management

The library is simplifying write database access layer in Go programs by supplying ORM approach - defining Entities and Repositories with minimal required code and utilize them during your regular development without needs to write a lot of boilerplate code.

Unlike other ORM libraries and frameworks e.g. Gorm etc. this library doesn't use reflection and have other performance relevant code optimisation therefore has no performance penalty comparing to all-by-hand written code

Examples

1. Create specific entity/repository

Up to date working examples locates here and here


package repository

import (
	"context"
	sq "github.com/Masterminds/squirrel"
	"github.com/jackc/pgx/v5"
	"github.com/simpleGorm/pg"
)

// Mandatory declare ID field
type TestObj struct {
	ID     int64
	Field1 int64
	Field2 string
}

type TestObjRepository struct {
	pg.Repository[TestObj]
}

const TABLE_NAME = "TestObjTable"

var increaseField1Builder = sq.Update(TABLE_NAME).PlaceholderFormat(sq.Dollar).
	Set("field1", sq.Expr("field1 + 1")).Suffix("RETURNING id, field1, field2")

func NewMyObjRepository(db pg.DbClient) TestObjRepository {
	return TestObjRepository{
		pg.NewPostgreRepository[TestObj](
			db,
			sq.Insert(TABLE_NAME).PlaceholderFormat(sq.Dollar).Columns("field1", "field2"),
			sq.Select("id", "field1", "field2").PlaceholderFormat(sq.Dollar).From(TABLE_NAME),
			sq.Update(TABLE_NAME).PlaceholderFormat(sq.Dollar),
			sq.Delete(TABLE_NAME).PlaceholderFormat(sq.Dollar),
			myObjConverter),
	}
}

func myObjConverter(row pgx.Row) TestObj {
	var myObj TestObj
	if err := row.Scan(&myObj.ID, &myObj.Field1, &myObj.Field2); err != nil {
		panic(err)
	}
	return myObj
}

func (repo *TestObjRepository) GetOneByField2(ctx context.Context, field2 string) TestObj {
	list := repo.GetBy(ctx, sq.Eq{"field2": field2})
	if list != nil && len(list) > 0 {
		return (list)[0]
	}
	var ret TestObj
	return ret
}

func (repo *TestObjRepository) IncreaseField1(ctx context.Context, id int64) int64 {
	updated := repo.UpdateReturning(ctx, increaseField1Builder.Where(sq.Eq{"id": id}))
	return updated.Field1
}

2. Usage

Up to date working examples locates here and here

    DSN := "connection string"
    ctx := context.Background()
    dbClient, err := pg.NewPgDBClient(ctx, DSN)
    
    // Define dbclient gracefull shutdown
    closer.Add(dbClient.Close)
    defer func() {
        if r := recover(); r != nil {
            // handle panic errors
        }
        // Close db connection
        closer.CloseAll()
        closer.Wait()
    }()
	
    myRepository := NewMyRepository(dbClient)
    field1_value := 10
    field2_value := "field2_value"
    id := myRepository.Create(ctx, field1_value, field2_value)
    ....
    myRepository.GetById(ctx,id)
    ....
    myRepository.GetByField2(ctx, field2_value)
    ....
    myRepository.GetAll(ctx)
    ....
    newField1Value := myRepository.IncreaseField1(ctx, id)
    print(newField1Value) // 11
    .....
    
   fields := map[string]interface{}{"field2": "updated_field2"}

   updated := myRepository.Update(ctx, fields, id)
   if updated != 1 {
      return errors.New("MyObject not updated")
   }
   
   where := squirrel.Eq{"field2": field2}
   updated = myRepository.UpdateCollection(ctx, fields, where)
   if updated == 0 {
      return errors.New("No objects was updated")
   }
    
    // Transaction
    err := dbClient.RunTransaction(ctx, transaction.TxOptions{IsoLevel: transaction.ReadCommitted},
		func(ctx context.Context) error {
			id1 := myRepository.Create(ctx, ...)
			id2 := myRepository.Create(ctx, ...)
			if(...some error condition...){
			    // Rollback
			    return errors.Wrap(err, "Error condition achieved")
			}
			// Commit
			return nil
		}
   )
   if err != nil {
     ...  // handle the error
   }

Documentation

Index

Constants

View Source
const (
	RETURNING_ID = "RETURNING id"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Closer added in v1.0.49

type Closer interface {
	Close() error
}

type DbApi

type DbApi interface {
	pg_api.SQLExecutor
	pg_api.Transactor
	Pinger
	Closer
}

type DbClient

type DbClient struct {
	DbApi
}

func NewDBClient

func NewDBClient(ctx context.Context, dsn string) (DbClient, error)

func (DbClient) Logger added in v1.0.49

func (db DbClient) Logger() *slog.Logger

func (DbClient) SetLogger added in v1.0.49

func (db DbClient) SetLogger(l *slog.Logger)

type IRepository

type IRepository interface {
	Create(ctx context.Context, values ...interface{}) int64
	GetById(ctx context.Context, id int64) any
	GetAll(ctx context.Context) []any
	GetBy(ctx context.Context, where sq.Sqlizer) []any
	Delete(ctx context.Context, id int64) int64
	UpdateCollection(ctx context.Context, fields map[string]interface{}, where sq.Sqlizer) int64
	Update(ctx context.Context, fields map[string]interface{}, id int64) int64
	UpdateReturning(ctx context.Context, builder sq.UpdateBuilder, entityConverter func(row pgx.Row) any) any
}

type Identifiable

type Identifiable interface {
	GetID() int64
}

type Pinger

type Pinger interface {
	Ping(ctx context.Context) error
}
type Related interface {
	GetParentID() int64
	PushToParent(parent any)
}

type Relation

type Relation[R any] struct {
	ForeignKey     string
	Repo           Repository[R]
	ParentIdGetter func(R) int64
}

func WrapRelation

func WrapRelation[R any](r Relation[R]) Relation[any]

func (Relation[R]) GetForeignKey

func (r Relation[R]) GetForeignKey() string

func (Relation[R]) GetParentId

func (r Relation[R]) GetParentId(child R) int64

type Repository

type Repository[T any] struct {
	DB            DbClient
	InsertBuilder sq.InsertBuilder
	SelectBuilder sq.SelectBuilder
	UpdateBuilder sq.UpdateBuilder
	DeleteBuilder sq.DeleteBuilder
	Converter     func(row pgx.Row) any // type is any to allow generalization
	Relations     []Relation[any]       // the relation type is any because it really any entity
	AddRelated    func(*T, any)
	AddRelation   func(Relation[any])
	// contains filtered or unexported fields
}

func NewRepository

func NewRepository[T any](
	anchor T,
	db DbClient,
	insertBuilder sq.InsertBuilder,
	selectBuilder sq.SelectBuilder,
	updateBuilder sq.UpdateBuilder,
	deleteBuilder sq.DeleteBuilder,
	converter func(row pgx.Row) *T) Repository[T]

func WrapRepository

func WrapRepository[R any](repo Repository[R]) Repository[any]

func (Repository[T]) Create

func (repo Repository[T]) Create(ctx context.Context, values ...interface{}) int64

func (Repository[T]) Delete

func (repo Repository[T]) Delete(ctx context.Context, id int64) int64

func (Repository[T]) GetAll

func (repo Repository[T]) GetAll(ctx context.Context) []T

func (Repository[T]) GetBy

func (repo Repository[T]) GetBy(ctx context.Context, where sq.Sqlizer) []T

func (Repository[T]) GetById

func (repo Repository[T]) GetById(ctx context.Context, id int64) T

func (Repository[T]) Update

func (repo Repository[T]) Update(ctx context.Context, fields map[string]interface{}, id int64) int64

func (Repository[T]) UpdateCollection

func (repo Repository[T]) UpdateCollection(ctx context.Context, fields map[string]interface{}, where sq.Sqlizer) int64

func (Repository[T]) UpdateReturning

func (repo Repository[T]) UpdateReturning(ctx context.Context, builder sq.UpdateBuilder) any

func (Repository[T]) UpdateReturningWithExtendedConverter

func (repo Repository[T]) UpdateReturningWithExtendedConverter(ctx context.Context, builder sq.UpdateBuilder, entityConverter func(row pgx.Row) any) any

Directories

Path Synopsis
internal
pkg

Jump to

Keyboard shortcuts

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