Documentation
¶
Overview ¶
Package data_base предоставляет типобезопасные, расширяемые и производительные компоненты для работы с реляционными базами данных в Go.
Основные возможности:
- Универсальные компоненты для CRUD операций: Finder, Saver, Deleter и др.
- Типобезопасные фильтры и сортировка с поддержкой распространённых операторов.
- Поддержка пагинации через компонент Pager.
- Гибкое преобразование между моделями и SQL-записями через функции-мапперы.
Все компоненты используют стандартный интерфейс dbClient.Client, что упрощает тестирование и позволяет легко подменять реализацию хранения данных.
Index ¶
- Variables
- func NewFilter(filter *repository.Filter) (goqu.Expression, error)
- func NewSort(sort *repository.Sort) exp.OrderedExpression
- type BulkInserter
- type Counter
- type Deleter
- type Finder
- type GetIdentifier
- type Inserter
- type Mapper
- type Pager
- type Saver
- type Scanner
- type SetIdentifier
- type Updater
Constants ¶
This section is empty.
Variables ¶
var Component = compogo.Component{ Dependencies: compogo.Components{ &dbClient.Component, &sqlGenerator.Component, &dbMigrator.Component, }, }
var FilterTypeUndefined = errors.New("undefined")
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 ¶
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 ¶
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 (*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
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]
type Mapper ¶ added in v0.0.4
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 (*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]
type Scanner ¶ added in v0.0.4
Scanner — сигнатура функции для преобразования строки результата SQL-запроса (*sql.Rows), используется в экземпляр модели типа T. Используется компонентами Finder и Pager.
Возвращает указатель на модель и ошибку, если сканирование не удалось.
type SetIdentifier ¶ added in v0.0.4
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]