Представляем Bun: ORM Golang

Представляем 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 или популярной альтернативы.



Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE