data_base

package module
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: MIT Imports: 11 Imported by: 0

README

Data Base

import "github.com/Compogo/data_base"

Package data_base предоставляет типобезопасные, расширяемые и производительные компоненты для работы с реляционными базами данных в Go.

Основные возможности:

  • Универсальные компоненты для CRUD операций: Finder, Saver, Deleter и др.
  • Типобезопасные фильтры и сортировка с поддержкой распространённых операторов.
  • Поддержка пагинации через компонент Pager.
  • Гибкое преобразование между моделями и SQL-записями через функции-мапперы.

Все компоненты используют стандартный интерфейс dbClient.Client, что упрощает тестирование и позволяет легко подменять реализацию хранения данных.

Index

Variables

var Component = compogo.Component{
    Dependencies: compogo.Components{
        &dbClient.Component,
        &sqlGenerator.Component,
        &dbMigrator.Component,
    },
}

var FilterTypeUndefined = errors.New("undefined")

IdNotBeZeroError возвращается компонентом Updater (и другими операциями обновления), если переданная модель имеет нулевой идентификатор (GetId() == 0).

Такая проверка позволяет избежать случайного обновления всех записей в таблице (например, при WHERE id = 0) и даёт явный сигнал о некорректном вызове.

Пример:

user := &User{Name: "Alice"} // ID не установлен
_, err := updater.Update(ctx, user)
if errors.Is(err, data_base.IdNotBeZeroError) {
    // Обработка ошибки: ID обязателен для обновления
}
var IdNotBeZeroError = errors.New("id must not be null")

func NewFilter

func NewFilter(filter *repository.Filter) (goqu.Expression, error)

NewFilter преобразует Filter из пакета repository в goqu.Expression, который можно использовать для построения SQL-запросов. Поддерживает операторы: =, !=, >, >=, <, <=, LIKE, IN.

Возвращает ошибку FilterTypeUndefined, если переданный оператор не поддерживается.

Пример:

filter := repository.NewFilter("name", "Alice", repository.Eq)
expr, err := data_base.NewFilter(filter)

func NewSort

func NewSort(sort *repository.Sort) exp.OrderedExpression

NewSort преобразует Sort из пакета repository в goqu.OrderedExpression для сортировки результатов запроса.

Поддерживает направления: ASC (по умолчанию) и DESC.

Пример:

sort := &repository.Sort{ColumnName: "created_at", Direction: repository.DESC}
expr := data_base.NewSort(sort)

type BulkInserter

BulkInserter предназначен для массовой вставки записей (INSERT) одной операцией. Принимает срез указателей на модели и преобразует их в записи с помощью Mapper.

Значительно повышает производительность при вставке большого количества записей, сокращая количество сетевых вызовов к базе данных.

Пример:

bulk := &data_base.BulkInserter[User]{
    tableName: "users",
    mapper: toRecord,
    db: db,
    gen: gen,
}
users := []*User{&User{Name: "A"}, &User{Name: "B"}}
err := bulk.BulkInsert(ctx, users...)
type BulkInserter[T any] struct {
    // contains filtered or unexported fields
}

func (*BulkInserter[T]) BulkInsert
func (b *BulkInserter[T]) BulkInsert(ctx context.Context, models ...*T) error

type Counter

Counter предназначен для подсчёта количества записей в таблице, удовлетворяющих переданным фильтрам.

Игнорирует значения NULL в указанной колонке для подсчёта. Для nil-фильтров возвращает общее количество записей в таблице.

Пример:

counter := data_base.NewCounter(db, gen, "users", "id")
filters := []*repository.Filter{repository.NewFilter("active", "true", repository.Eq)}
count, err := counter.Count(ctx, filters...)
type Counter struct {
    // contains filtered or unexported fields
}

func NewCounter
func NewCounter(db dbClient.Client, gen *goqu.DialectWrapper, tableName string, counterColName string) *Counter

func (*Counter) Count
func (c *Counter) Count(ctx context.Context, filters ...*repository.Filter) (uint64, error)

type Deleter

Deleter предназначен для удаления записей из таблицы по переданным фильтрам. Для выполнения физического удаления (DELETE) используйте фильтры. Для мягкого удаления рекомендуется использовать Updater.

Возвращает ошибку, если удаление не удалось выполнить.

Пример:

deleter := data_base.NewDeleter(db, gen, "users")
filters := []*repository.Filter{repository.NewFilter("id", 123, repository.Eq)}
err := deleter.Delete(ctx, filters...)
type Deleter struct {
    // contains filtered or unexported fields
}

func NewDeleter
func NewDeleter(db dbClient.Client, gen *goqu.DialectWrapper, tableName string) *Deleter

func (*Deleter) Delete
func (d *Deleter) Delete(ctx context.Context, filters ...*repository.Filter) error

type Finder

Finder предназначен для поиска записей в базе данных с поддержкой фильтрации и сортировки. Возвращает срез указателей на модели типа T.

Нулевое значение структуры использовать нельзя. Для создания экземпляра используйте функцию NewFinder, передавая в неё экземпляр клиента БД, генератор SQL, функцию для маппинга строки результата в модель и название таблицы.

Пример:

rowMapper := func(rows *sql.Rows) (*User, error) {
    var u User
    err := rows.Scan(&u.ID, &u.Name, &u.Email)
    return &u, err
}

finder := data_base.NewFinder[User](db, goqu.Dialect("postgres"), rowMapper, "users", "id", "name", "email")
filters := []*repository.Filter{repository.NewFilter("name", "Alice", repository.Eq)}
users, err := finder.Find(ctx, nil, filters...)
type Finder[T any] struct {
    // contains filtered or unexported fields
}

func NewFinder
func NewFinder[T any](db dbClient.Client, gen *goqu.DialectWrapper, scanner Scanner[T], tableName string, columns ...any) *Finder[T]

func (*Finder[T]) Find
func (f *Finder[T]) Find(ctx context.Context, sorts []*repository.Sort, filters ...*repository.Filter) ([]*T, error)

type GetIdentifier

GetIdentifier — сигнатура функции для извлечения числового идентификатора из модели.

Используется компонентами Saver и Updater для определения, является ли модель новой (ID == 0) или уже существующей (ID != 0).

Пример:

getID := func(m *User) uint64 { return m.ID }
id := getID(user) // 12345

Обычно такой хелпер передаётся в конструктор:

saver := data_base.NewSaver(
    db, gen,
    mapper,
    getID,
    setID,
    "users", "id",
)
type GetIdentifier[T any] func(*T) uint64

type Inserter

Inserter предназначен только для вставки новых записей (INSERT). После успешной вставки автоматически устанавливает сгенерированный ID модели через SetId().

В отличие от Saver, не выполняет обновление при наличии ID. Полезен для сценариев, где нужно гарантировать создание новой записи.

Пример:

inserter := data_base.NewInserter[User](db, gen, toRecord, "users")
user := &User{Name: "Bob", Email: "bob@example.com"}
insertedUser, err := inserter.Insert(ctx, user)
type Inserter[T any] struct {
    // contains filtered or unexported fields
}

func NewInserter
func NewInserter[T any](db dbClient.Client, gen *goqu.DialectWrapper, mapper Mapper[T], setIdentifier SetIdentifier[T], tableName string) *Inserter[T]

func (*Inserter[T]) Insert
func (i *Inserter[T]) Insert(ctx context.Context, model *T) (*T, error)

type Mapper

Mapper — сигнатура функции для преобразования указателя на модель, используется в запись для SQL-запроса (goqu.Record). Используется компонентами Saver, Inserter, Updater и BulkInserter.

Позволяет гибко контролировать, какие поля и как сохраняются в базе данных.

type Mapper[T any] func(*T) goqu.Record

type Pager

Pager предназначен для постраничного получения данных с фильтрацией и сортировкой. Является расширением Finder и использует тот же механизм маппинга строк в модели.

Поддерживает все виды фильтров и сортировок из пакета repository. Для работы с пагинацией используйте структуру repository.Page.

Пример:

pager := data_base.NewPager(db, gen, rowMapper, "users", "id", "name")
page := &repository.Page{Number: 0, Limit: 10}
users, err := pager.Page(ctx, page, sorts, filters...)
type Pager[T any] struct {
    // contains filtered or unexported fields
}

func NewPager
func NewPager[T any](db dbClient.Client, gen *goqu.DialectWrapper, scanner Scanner[T], tableName string, columns ...any) *Pager[T]

func (*Pager[T]) Page
func (p *Pager[T]) Page(ctx context.Context, page *repository.Page, sorts []*repository.Sort, filters ...*repository.Filter) ([]*T, error)

type Saver

Saver реализует паттерн "сохранить или обновить" (UPSERT), автоматически определяя операцию по значению идентификатора модели.

Если GetId() возвращает 0, выполняется вставка (INSERT) новой записи, после чего модели автоматически устанавливается сгенерированный ID через SetId(). Если GetId() не равен 0, выполняется обновление (UPDATE) существующей записи.

Требует, чтобы тип T реализовывал интерфейс Identifier. Для работы необходима функция, преобразующая модель в goqu.Record.

Пример:

toRecord := func(u *User) goqu.Record {
    return goqu.Record{"name": u.Name, "email": u.Email}
}

saver := data_base.NewSaver[User](db, gen, toRecord, "users", "id")
user := &User{Name: "Alice", Email: "alice@example.com"}
savedUser, err := saver.Save(ctx, user) // user.ID будет установлен после вставки
type Saver[T any] struct {
    // contains filtered or unexported fields
}

func NewSaver
func NewSaver[T any](db dbClient.Client, gen *goqu.DialectWrapper, mapper Mapper[T], getIdentifier GetIdentifier[T], setIdentifier SetIdentifier[T], tableName string, idColName string) *Saver[T]

func (*Saver[T]) Save
func (s *Saver[T]) Save(ctx context.Context, model *T) (*T, error)

type Scanner

Scanner — сигнатура функции для преобразования строки результата SQL-запроса (*sql.Rows), используется в экземпляр модели типа T. Используется компонентами Finder и Pager.

Возвращает указатель на модель и ошибку, если сканирование не удалось.

type Scanner[T any] func(*sql.Rows) (*T, error)

type SetIdentifier

SetIdentifier — сигнатура функции для установки числового идентификатора в модель.

Используется компонентами Saver и Inserter для проставления сгенерированного базой данных ID (например, после INSERT с auto_increment или serial колонкой).

Важно: функция должна изменять переданную модель (принимает указатель), а не возвращать новое значение.

Пример:

setID := func(m *User, id uint64) { m.ID = id }
setID(user, 12345)

Обычно такой хелпер передаётся в конструктор:

inserter := data_base.NewInserter(
    db, gen,
    mapper,
    setID,
    "users",
)
type SetIdentifier[T any] func(*T, uint64)

type Updater

Updater предназначен для обновления существующих записей в таблице. В отличие от Saver, Updater не выполняет вставку новых записей и требует, чтобы переданная модель имела заполненный идентификатор (GetId() != 0).

Если идентификатор модели равен 0, метод Update возвращает ошибку IdNotBeZeroError. Это предотвращает случайное выполнение UPDATE без условия WHERE.

Компонент использует функцию Mapper для преобразования модели в запись (goqu.Record) и выполняет обновление по указанному полю идентификатора.

Пример использования:

toRecord := func(u *User) goqu.Record {
    return goqu.Record{"name": u.Name, "email": u.Email}
}

updater := data_base.NewUpdater(
    db,
    goqu.Dialect("postgres"),
    toRecord,
    "users",
    "id",
)

user := &User{Id: 123, Name: "Updated Name"}
updatedUser, err := updater.Update[User](ctx, user)
if err != nil {
    // Обработка ошибки
}
type Updater[T any] struct {
    // contains filtered or unexported fields
}

func NewUpdater
func NewUpdater[T any](db dbClient.Client, gen *goqu.DialectWrapper, mapper Mapper[T], getIdentifier GetIdentifier[T], tableName string, idColName string) *Updater[T]

func (*Updater[T]) Update
func (u *Updater[T]) Update(ctx context.Context, model *T) (*T, error)

Generated by gomarkdoc

Documentation

Overview

Package data_base предоставляет типобезопасные, расширяемые и производительные компоненты для работы с реляционными базами данных в Go.

Основные возможности:

  • Универсальные компоненты для CRUD операций: Finder, Saver, Deleter и др.
  • Типобезопасные фильтры и сортировка с поддержкой распространённых операторов.
  • Поддержка пагинации через компонент Pager.
  • Гибкое преобразование между моделями и SQL-записями через функции-мапперы.

Все компоненты используют стандартный интерфейс dbClient.Client, что упрощает тестирование и позволяет легко подменять реализацию хранения данных.

Index

Constants

This section is empty.

Variables

View Source
var FilterTypeUndefined = errors.New("undefined")
View Source
var IdNotBeZeroError = errors.New("id must not be null")

IdNotBeZeroError возвращается компонентом Updater (и другими операциями обновления), если переданная модель имеет нулевой идентификатор (GetId() == 0).

Такая проверка позволяет избежать случайного обновления всех записей в таблице (например, при WHERE id = 0) и даёт явный сигнал о некорректном вызове.

Пример:

user := &User{Name: "Alice"} // ID не установлен
_, err := updater.Update(ctx, user)
if errors.Is(err, data_base.IdNotBeZeroError) {
    // Обработка ошибки: ID обязателен для обновления
}

Functions

func NewFilter

func NewFilter(filter *repository.Filter) (goqu.Expression, error)

NewFilter преобразует Filter из пакета repository в goqu.Expression, который можно использовать для построения SQL-запросов. Поддерживает операторы: =, !=, >, >=, <, <=, LIKE, IN.

Возвращает ошибку FilterTypeUndefined, если переданный оператор не поддерживается.

Пример:

filter := repository.NewFilter("name", "Alice", repository.Eq)
expr, err := data_base.NewFilter(filter)

func NewSort

func NewSort(sort *repository.Sort) exp.OrderedExpression

NewSort преобразует Sort из пакета repository в goqu.OrderedExpression для сортировки результатов запроса.

Поддерживает направления: ASC (по умолчанию) и DESC.

Пример:

sort := &repository.Sort{ColumnName: "created_at", Direction: repository.DESC}
expr := data_base.NewSort(sort)

Types

type BulkInserter

type BulkInserter[T any] struct {
	// contains filtered or unexported fields
}

BulkInserter предназначен для массовой вставки записей (INSERT) одной операцией. Принимает срез указателей на модели и преобразует их в записи с помощью Mapper.

Значительно повышает производительность при вставке большого количества записей, сокращая количество сетевых вызовов к базе данных.

Пример:

bulk := &data_base.BulkInserter[User]{
    tableName: "users",
    mapper: toRecord,
    db: db,
    gen: gen,
}
users := []*User{&User{Name: "A"}, &User{Name: "B"}}
err := bulk.BulkInsert(ctx, users...)

func (*BulkInserter[T]) BulkInsert

func (b *BulkInserter[T]) BulkInsert(ctx context.Context, models ...*T) error

type Counter

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

Counter предназначен для подсчёта количества записей в таблице, удовлетворяющих переданным фильтрам.

Игнорирует значения NULL в указанной колонке для подсчёта. Для nil-фильтров возвращает общее количество записей в таблице.

Пример:

counter := data_base.NewCounter(db, gen, "users", "id")
filters := []*repository.Filter{repository.NewFilter("active", "true", repository.Eq)}
count, err := counter.Count(ctx, filters...)

func NewCounter

func NewCounter(db dbClient.Client, gen *goqu.DialectWrapper, tableName string, counterColName string) *Counter

func (*Counter) Count

func (c *Counter) Count(ctx context.Context, filters ...*repository.Filter) (uint64, error)

type Deleter

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

Deleter предназначен для удаления записей из таблицы по переданным фильтрам. Для выполнения физического удаления (DELETE) используйте фильтры. Для мягкого удаления рекомендуется использовать Updater.

Возвращает ошибку, если удаление не удалось выполнить.

Пример:

deleter := data_base.NewDeleter(db, gen, "users")
filters := []*repository.Filter{repository.NewFilter("id", 123, repository.Eq)}
err := deleter.Delete(ctx, filters...)

func NewDeleter

func NewDeleter(db dbClient.Client, gen *goqu.DialectWrapper, tableName string) *Deleter

func (*Deleter) Delete

func (d *Deleter) Delete(ctx context.Context, filters ...*repository.Filter) error

type Finder

type Finder[T any] struct {
	// contains filtered or unexported fields
}

Finder предназначен для поиска записей в базе данных с поддержкой фильтрации и сортировки. Возвращает срез указателей на модели типа T.

Нулевое значение структуры использовать нельзя. Для создания экземпляра используйте функцию NewFinder, передавая в неё экземпляр клиента БД, генератор SQL, функцию для маппинга строки результата в модель и название таблицы.

Пример:

rowMapper := func(rows *sql.Rows) (*User, error) {
    var u User
    err := rows.Scan(&u.ID, &u.Name, &u.Email)
    return &u, err
}

finder := data_base.NewFinder[User](db, goqu.Dialect("postgres"), rowMapper, "users", "id", "name", "email")
filters := []*repository.Filter{repository.NewFilter("name", "Alice", repository.Eq)}
users, err := finder.Find(ctx, nil, filters...)

func NewFinder

func NewFinder[T any](db dbClient.Client, gen *goqu.DialectWrapper, scanner Scanner[T], tableName string, columns ...any) *Finder[T]

func (*Finder[T]) Find

func (f *Finder[T]) Find(ctx context.Context, sorts []*repository.Sort, filters ...*repository.Filter) ([]*T, error)

type GetIdentifier added in v0.0.4

type GetIdentifier[T any] func(*T) uint64

GetIdentifier — сигнатура функции для извлечения числового идентификатора из модели.

Используется компонентами Saver и Updater для определения, является ли модель новой (ID == 0) или уже существующей (ID != 0).

Пример:

getID := func(m *User) uint64 { return m.ID }
id := getID(user) // 12345

Обычно такой хелпер передаётся в конструктор:

saver := data_base.NewSaver(
    db, gen,
    mapper,
    getID,
    setID,
    "users", "id",
)

type Inserter

type Inserter[T any] struct {
	// contains filtered or unexported fields
}

Inserter предназначен только для вставки новых записей (INSERT). После успешной вставки автоматически устанавливает сгенерированный ID модели через SetId().

В отличие от Saver, не выполняет обновление при наличии ID. Полезен для сценариев, где нужно гарантировать создание новой записи.

Пример:

inserter := data_base.NewInserter[User](db, gen, toRecord, "users")
user := &User{Name: "Bob", Email: "bob@example.com"}
insertedUser, err := inserter.Insert(ctx, user)

func NewInserter

func NewInserter[T any](
	db dbClient.Client,
	gen *goqu.DialectWrapper,
	mapper Mapper[T],
	setIdentifier SetIdentifier[T],
	tableName string,
) *Inserter[T]

func (*Inserter[T]) Insert

func (i *Inserter[T]) Insert(ctx context.Context, model *T) (*T, error)

type Mapper added in v0.0.4

type Mapper[T any] func(*T) goqu.Record

Mapper — сигнатура функции для преобразования указателя на модель, используется в запись для SQL-запроса (goqu.Record). Используется компонентами Saver, Inserter, Updater и BulkInserter.

Позволяет гибко контролировать, какие поля и как сохраняются в базе данных.

type Pager

type Pager[T any] struct {
	// contains filtered or unexported fields
}

Pager предназначен для постраничного получения данных с фильтрацией и сортировкой. Является расширением Finder и использует тот же механизм маппинга строк в модели.

Поддерживает все виды фильтров и сортировок из пакета repository. Для работы с пагинацией используйте структуру repository.Page.

Пример:

pager := data_base.NewPager(db, gen, rowMapper, "users", "id", "name")
page := &repository.Page{Number: 0, Limit: 10}
users, err := pager.Page(ctx, page, sorts, filters...)

func NewPager

func NewPager[T any](db dbClient.Client, gen *goqu.DialectWrapper, scanner Scanner[T], tableName string, columns ...any) *Pager[T]

func (*Pager[T]) Page

func (p *Pager[T]) Page(ctx context.Context, page *repository.Page, sorts []*repository.Sort, filters ...*repository.Filter) ([]*T, error)

type Saver

type Saver[T any] struct {
	// contains filtered or unexported fields
}

Saver реализует паттерн "сохранить или обновить" (UPSERT), автоматически определяя операцию по значению идентификатора модели.

Если GetId() возвращает 0, выполняется вставка (INSERT) новой записи, после чего модели автоматически устанавливается сгенерированный ID через SetId(). Если GetId() не равен 0, выполняется обновление (UPDATE) существующей записи.

Требует, чтобы тип T реализовывал интерфейс Identifier. Для работы необходима функция, преобразующая модель в goqu.Record.

Пример:

toRecord := func(u *User) goqu.Record {
    return goqu.Record{"name": u.Name, "email": u.Email}
}

saver := data_base.NewSaver[User](db, gen, toRecord, "users", "id")
user := &User{Name: "Alice", Email: "alice@example.com"}
savedUser, err := saver.Save(ctx, user) // user.ID будет установлен после вставки

func NewSaver

func NewSaver[T any](
	db dbClient.Client,
	gen *goqu.DialectWrapper,
	mapper Mapper[T],
	getIdentifier GetIdentifier[T],
	setIdentifier SetIdentifier[T],
	tableName string,
	idColName string,
) *Saver[T]

func (*Saver[T]) Save

func (s *Saver[T]) Save(ctx context.Context, model *T) (*T, error)

type Scanner added in v0.0.4

type Scanner[T any] func(*sql.Rows) (*T, error)

Scanner — сигнатура функции для преобразования строки результата SQL-запроса (*sql.Rows), используется в экземпляр модели типа T. Используется компонентами Finder и Pager.

Возвращает указатель на модель и ошибку, если сканирование не удалось.

type SetIdentifier added in v0.0.4

type SetIdentifier[T any] func(*T, uint64)

SetIdentifier — сигнатура функции для установки числового идентификатора в модель.

Используется компонентами Saver и Inserter для проставления сгенерированного базой данных ID (например, после INSERT с auto_increment или serial колонкой).

Важно: функция должна изменять переданную модель (принимает указатель), а не возвращать новое значение.

Пример:

setID := func(m *User, id uint64) { m.ID = id }
setID(user, 12345)

Обычно такой хелпер передаётся в конструктор:

inserter := data_base.NewInserter(
    db, gen,
    mapper,
    setID,
    "users",
)

type Updater

type Updater[T any] struct {
	// contains filtered or unexported fields
}

Updater предназначен для обновления существующих записей в таблице. В отличие от Saver, Updater не выполняет вставку новых записей и требует, чтобы переданная модель имела заполненный идентификатор (GetId() != 0).

Если идентификатор модели равен 0, метод Update возвращает ошибку IdNotBeZeroError. Это предотвращает случайное выполнение UPDATE без условия WHERE.

Компонент использует функцию Mapper для преобразования модели в запись (goqu.Record) и выполняет обновление по указанному полю идентификатора.

Пример использования:

toRecord := func(u *User) goqu.Record {
    return goqu.Record{"name": u.Name, "email": u.Email}
}

updater := data_base.NewUpdater(
    db,
    goqu.Dialect("postgres"),
    toRecord,
    "users",
    "id",
)

user := &User{Id: 123, Name: "Updated Name"}
updatedUser, err := updater.Update[User](ctx, user)
if err != nil {
    // Обработка ошибки
}

func NewUpdater

func NewUpdater[T any](
	db dbClient.Client,
	gen *goqu.DialectWrapper,
	mapper Mapper[T],
	getIdentifier GetIdentifier[T],
	tableName string,
	idColName string,
) *Updater[T]

func (*Updater[T]) Update

func (u *Updater[T]) Update(ctx context.Context, model *T) (*T, error)

Jump to

Keyboard shortcuts

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