Представляем Bun: ORM Golang
27 апреля 2022 г.SQL-первый
Bun — это [SQL-first Golang ORM] (https://bun.uptrace.dev/) для PostgreSQL, MySQL/MariaDB, MSSQL и SQLite.
SQL-first означает, что вы можете писать SQL-запросы в Go, например, этот запрос Bun:
```иди
номер переменной целое
ошибка := db.NewSelect().
TableExpr("генерировать_серии(1, 3)").
Где("генерировать_серии =?", 3).
Предел(10).
Сканировать(ctx, &num)
Генерирует следующий SQL:
```sql
ВЫБРАТЬ *
ИЗ generate_series(1, 3)
ГДЕ generate_series = 123
ПРЕДЕЛ 10
SQL по-прежнему присутствует, но Bun помогает генерировать длинные запросы, защищая от SQL-инъекций благодаря ?
[заполнителям] (https://bun.uptrace.dev/guide/placeholders.html):
```иди
Где("id = ?", 123) // ГДЕ id = 123
Где("id >= ?", 123) // ГДЕ id >= 123
Где("id = ?", "привет") // ГДЕ id = 'привет'
Where("id IN (?)", bun.In([]int{1, 2, 3})) // ГДЕ id IN (1, 2, 3)
Где("? = ?", bun.Ident("столбец"), "значение") // ГДЕ "столбец" = 'значение'
Используя Bun, вы можете писать действительно сложные запросы, например, следующий запрос Bun:
```иди
RegionalSales := db.NewSelect().
ВыражениеСтолбца("регион").
ColumnExpr("СУММА(сумма) КАК total_sales").
TableExpr("заказы").
GroupExpr("регион")
topRegions := db.NewSelect().
ВыражениеСтолбца("регион").
TableExpr("region_sales").
Где("общие_продажи > (ВЫБЕРИТЕ СУММУ(общие_продажи) / 10 ИЗ региональных_продаж)")
var []items map[string]интерфейс{}
ошибка := db.NewSelect().
С("region_sales", RegionalSales).
With("top_regions", topRegions).
ВыражениеСтолбца("регион").
ВыражениеСтолбца("продукт").
ColumnExpr("СУММА(количество) КАК product_units").
ColumnExpr("СУММА(сумма) КАК product_sales").
TableExpr("заказы").
Где("регион В (ВЫБЕРИТЕ регион ИЗ top_regions)").
GroupExpr("регион").
ГруппЭкспр("продукт").
Сканировать(ctx, &items)
Генерирует следующий SQL:
```sql
С Regional_sales AS (
ВЫБЕРИТЕ регион, СУММА (сумма) КАК total_sales
ОТ заказов
СГРУППИРОВАТЬ ПО РЕГИОНАМ
), топ_регионы AS (
ВЫБЕРИТЕ регион
ОТ регионарных_продаж
ГДЕ total_sales > (ВЫБЕРИТЕ СУММУ(total_sales)/10 FROM Regional_sales)
ВЫБЕРИТЕ регион,
продукт,
SUM(количество) AS product_units,
СУММА(сумма) КАК product_sales
ОТ заказов
ГДЕ регион В (ВЫБЕРИТЕ регион ИЗ top_regions)
СГРУППИРОВАТЬ ПО РЕГИОНАМ, ТОВАРАМ
Структуры и таблицы
Bun позволяет сопоставлять структуры Go с таблицами базы данных, используя [модели] на основе структур (https://bun.uptrace.dev/guide/models.html), например, следующий код:
```иди
тип Модель структура {
ID int64 bun:",pk,autoincrement"
Строка имени bun:",notnull"
CreatedAt time.Time bun:",nullzero,default:now()"
ошибка: = db.ResetModel (ctx, & Model {})
Создает следующую таблицу:
```sql
СОЗДАТЬ ТАБЛИЦУ "модели" (
"id" BIGSERIAL НЕ NULL,
"имя" VARCHAR НЕ NULL,
"создано_в" TIMESTAMPTZ ПО УМОЛЧАНИЮ сейчас(),
ПЕРВИЧНЫЙ КЛЮЧ ("id"),
Затем вы можете выбирать/вставлять/обновлять/удалять строки, используя структуры Go:
```иди
модель: = новая (модель)
ошибка := db.NewSelect().Model().Where("id = ?", 123).Scan(ctx)
модель.ID = 0
res, err:= db.NewInsert().Model(model).Exec(ctx)
разрешение, ошибка := db.NewUpdate().
Модель (модель).
Установить("имя = ?", "обновленное имя").
ГдеПК().
Исполнение (ctx)
res, err := db.NewDelete().Model(model).WherePK().Exec(ctx)
Подробнее см. Документация по Bun.
Голанг ORM
Так что насчет Golang ORM? Bun позволяет вам определять общие табличные отношения с помощью структур Go, например, вот как вы можете определить, что «Автор» принадлежит отношению «Книга»:
```иди
тип книги структура {
ID int64
ID автора int64
Автор Автор bun:"rel:belongs-to,join:author_id=id"
тип Автор структура {
ID int64
А затем используйте метод Relation
для соединения таблиц:
```иди
ошибка := db.NewSelect().
Модель (книга).
Отношение("Автор").
Где("id = ?", 123).
Сканировать (ctx)
```иди
ВЫБРАТЬ
"книга"."id", "книга"."название", "книга"."текст",
"автор"."id" КАК "автор__ид", "автор"."имя" КАК "автор__имя"
ИЗ "книг"
LEFT JOIN "users" AS "author" ON "author"."id" = "book"."author_id"
ГДЕ идентификатор = 1
Подробнее см. ORM: Отношения между таблицами.
Подключение к базе данных
Bun работает поверх базы данных/SQL и поддерживает PostgreSQL, MySQL/MariaDB, MSSQL и SQLite.
Чтобы подключиться к базе данных PostgreSQL:
```иди
импорт (
"github.com/uptrace/бун"
"github.com/uptrace/bun/dialect/pgdialect"
"github.com/uptrace/бун/драйвер/pgdriver"
dsn: = "postgres://postgres:@localhost:5432/test?sslmode=disable"
sqldb: = sql.OpenDB (pgdriver.NewConnector (pgdriver.WithDSN (dsn)))
db := bun.NewDB(sqldb, pgdialect.New())
Чтобы подключиться к базе данных MySQL:
```иди
импорт (
"github.com/uptrace/бун"
"github.com/uptrace/bun/dialect/mysqldialect"
_ "github.com/go-sql-драйвер/mysql"
sqldb, err := sql.Open("mysql", "root:pass@/test")
если ошибка != ноль {
паника (ошибка)
db := bun.NewDB(sqldb, mysqldialect.New())
Чтобы регистрировать все выполненные запросы, вы можете установить плагин bundebug:
```иди
импортировать "github.com/uptrace/bun/extra/bundebug"
db.AddQueryHook(bundebug.NewQueryHook(
bundebug.WithVerbose(true), // логируем все
Выполнение запросов
Получив модель, вы можете начать выполнять запросы:
```иди
// Выбираем пользователя по первичному ключу.
пользователь: = новый (пользователь)
ошибка := db.NewSelect().Model(user).Where("id = ?", 1).Scan(ctx)
// Выбрать первых 10 пользователей.
var пользователей []Пользователь
ошибка := db.NewSelect().Model(&users).OrderExpr("id ASC").Limit(10).Scan(ctx)
Когда дело доходит до сканирования результатов запроса, Bun очень гибок и позволяет сканировать в структуры:
```иди
пользователь: = новый (пользователь)
ошибка := db.NewSelect().Model(user).Limit(1).Scan(ctx)
В скаляры:
```иди
идентификатор переменной int64
строка имени переменной
err := db.NewSelect().Model((*User)(nil)).Column("id", "name").Limit(1).Scan(ctx, &id, &name)
В map[string]interface{}
:
```иди
var m map[string]интерфейс{}
err := db.NewSelect().Model((*User)(nil)).Limit(1).Scan(ctx, &m)
И на слайсы типов выше:
```иди
var пользователей []Пользователь
ошибка := db.NewSelect().Model(&users).Limit(1).Scan(ctx)
переменные идентификаторы []int64
имена переменных [] строка
err := db.NewSelect().Model((*User)(nil)).Column("id", "name").Limit(1).Scan(ctx, &ids, &names)
var ms []карта[строка]интерфейс{}
ошибка := db.NewSelect().Model((*User)(nil)).Scan(ctx, &ms)
Вы также можете возвращать результаты запросов на вставку/обновление/удаление и сканировать их:
```иди
переменные идентификаторы []int64
res, err := db.NewDelete().Model((*User)(nil)).Returning("id").Exec(ctx, &ids)
Что дальше?
Для начала просмотрите документацию и запустите примеры.
Bun поставляется со многими подключаемыми модулями, включая инструментарий OpenTelemetry, который обеспечивает распределенную трассировку и метрики .
Используя трассировку, вы можете отслеживать производительность с помощью одного из инструментов трассировки с открытым исходным кодом, которые работают с OpenTelemetry. Многие [конкуренты DataDog] (https://get.uptrace.dev/compare/datadog-competitors.html) также поддерживают OpenTelemetry.
Кроме того, вы можете экспортировать метрики в Prometheus и визуализировать их с помощью Grafana или популярной альтернативы.
Оригинал