Bun: дружественный к SQL Golang ORM

от автора

Bun это дружественный к SQL Golang ORM для PostgreSQL, MySQL/MariaDB, MSSQL, и SQLite. Данная статья призвана познакомить вас с базовыми возможностями этой ORM.

Работа с SQL

Дружественный к SQL означает что вы можете элегантно писать стандартные SQL запросы используя язык Go. К примеру, следующий Bun запрос:

var num int err := db.NewSelect().     TableExpr("generate_series(1, 3)").     Where("generate_series = ?", 3).     Limit(10).     Scan(ctx, &num)

Генерирует такой SQL:

SELECT * FROM generate_series(1, 3) WHERE generate_series = 123 LIMIT 10

Как вы можете видеть, Bun запросы все еще содержат узнаваемый SQL, который к тому еще защищен от SQL инъекций благодаря использованию специальных ? placeholders:

Where("id = ?", 123)     // WHERE id = 123 Where("id >= ?", 123)    // WHERE id >= 123 Where("id = ?", "hello") // WHERE id = 'hello'  Where("id IN (?)", bun.In([]int{1, 2, 3})) // WHERE id IN (1, 2, 3)  Where("? = ?", bun.Ident("column"), "value") // WHERE "column" = 'value'

Используя Bun, вы можете писать действительно сложные запросы, к примеру, следующий Bun запрос:

regionalSales := db.NewSelect(). ColumnExpr("region"). ColumnExpr("SUM(amount) AS total_sales"). TableExpr("orders"). GroupExpr("region")  topRegions := db.NewSelect(). ColumnExpr("region"). TableExpr("regional_sales"). Where("total_sales > (SELECT SUM(total_sales) / 10 FROM regional_sales)")  var []items map[string]interface{}  err := db.NewSelect(). With("regional_sales", regionalSales). With("top_regions", topRegions). ColumnExpr("region"). ColumnExpr("product"). ColumnExpr("SUM(quantity) AS product_units"). ColumnExpr("SUM(amount) AS product_sales"). TableExpr("orders"). Where("region IN (SELECT region FROM top_regions)"). GroupExpr("region"). GroupExpr("product"). Scan(ctx, &items)

Генерирует такой SQL:

WITH regional_sales AS (     SELECT region, SUM(amount) AS total_sales     FROM orders     GROUP BY region ), top_regions AS (     SELECT region     FROM regional_sales     WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales) ) SELECT region,        product,        SUM(quantity) AS product_units,        SUM(amount) AS product_sales FROM orders WHERE region IN (SELECT region FROM top_regions) GROUP BY region, product

Структуры и таблицы

Bun позволяет отображать Go структуры и SQL таблицы используя модели, к примеру, следующий код:

type Model struct {     ID        int64 `bun:",pk,autoincrement"`     Name      string `bun:",notnull"`     CreatedAt time.Time `bun:",nullzero,default:now()"` }  err := db.ResetModel(ctx, &Model{})

Генерирует такую таблицу:

CREATE TABLE "models" (   "id" BIGSERIAL NOT NULL,   "name" VARCHAR NOT NULL,   "created_at" TIMESTAMPTZ DEFAULT now(),   PRIMARY KEY ("id"), )

Используя объявленную модель, вы можете генерировать select/insert/update/delete запросы:

model := new(Model) err := db.NewSelect().Model().Where("id = ?", 123).Scan(ctx)  model.ID = 0 res, err := db.NewInsert().Model(model).Exec(ctx)  res, err := db.NewUpdate().     Model(model).     Set("name = ?", "updated name").     WherePK().     Exec(ctx)  res, err := db.NewDelete().Model(model).WherePK().Exec(ctx)

Обращайтесь к Bun документации за подробностями.

Golang ORM

Как насчет части про Golang ORM? Bun позволяет объявлять популярные отношения между таблицами используя Go структуры. К примеру, вы можете описать Author принадлежит к Book таким образом:

type Book struct { ID int64 AuthorID int64 Author Author `bun:"rel:belongs-to,join:author_id=id"` }  type Author struct { ID int64 }

А затем использовать Relation метод для джойна таблицы:

err := db.NewSelect(). Model(book). Relation("Author"). Where("id = ?", 123). Scan(ctx)
SELECT   "book"."id", "book"."title", "book"."text",   "author"."id" AS "author__id", "author"."name" AS "author__name" FROM "books" LEFT JOIN "users" AS "author" ON "author"."id" = "book"."author_id" WHERE id = 1

За подробностями обращайтесь к ORM: Table relationships.

Соединение с базой данных

Bun работает поверх database/sql и поддерживает PostgreSQL, MySQL/MariaDB, MSSQL, и SQLite.

Для соединения с PostgreSQL базой данных:

import ( "github.com/uptrace/bun" "github.com/uptrace/bun/dialect/pgdialect"     "github.com/uptrace/bun/driver/pgdriver" )  dsn := "postgres://postgres:@localhost:5432/test?sslmode=disable" sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn)))  db := bun.NewDB(sqldb, pgdialect.New())

Для соединения с MySQL базой данных:

import ( "github.com/uptrace/bun" "github.com/uptrace/bun/dialect/mysqldialect" _ "github.com/go-sql-driver/mysql" )  sqldb, err := sql.Open("mysql", "root:pass@/test") if err != nil { panic(err) }  db := bun.NewDB(sqldb, mysqldialect.New())

Для логгирования всех обработанных запросов, вы можете установить bundebug плагин:

import "github.com/uptrace/bun/extra/bundebug"  db.AddQueryHook(bundebug.NewQueryHook( bundebug.WithVerbose(true), // log everything ))

Выполнение запросов

После того как вы объявили модель, вы можете начинать выполнять запросы:

// Select a user by a primary key. user := new(User) err := db.NewSelect().Model(user).Where("id = ?", 1).Scan(ctx)  // Select first 10 users. var users []User err := db.NewSelect().Model(&users).OrderExpr("id ASC").Limit(10).Scan(ctx)

Когда речь идет о сканировании результатов запросов, Bun очень гибок и позволяет работать со структурами:

user := new(User) err := db.NewSelect().Model(user).Limit(1).Scan(ctx)

Скалярами:

var id int64 var name string err := db.NewSelect().Model((*User)(nil)).Column("id", "name").Limit(1).Scan(ctx, &id, &name)

Хэш таблицами типа map[string]interface{}:

var m map[string]interface{} err := db.NewSelect().Model((*User)(nil)).Limit(1).Scan(ctx, &m)

И массивами всех ранее упомянутых типов:

var users []User err := db.NewSelect().Model(&users).Limit(1).Scan(ctx)  var ids []int64 var names []string err := db.NewSelect().Model((*User)(nil)).Column("id", "name").Limit(1).Scan(ctx, &ids, &names)  var ms []map[string]interface{} err := db.NewSelect().Model((*User)(nil)).Scan(ctx, &ms)

Вы также можете сканировать результаты insert/update/delete запросов:

var ids []int64 res, err := db.NewDelete().Model((*User)(nil)).Returning("id").Exec(ctx, &ids)

Мониторинг производетельности

Bun имеет множество плагинов включая OpenTelemetry инструментацию позволяющую использовать distributed tracing и метрики.

Трэйсинг позволяет вам мониторить производительность БД используя множество open source tracing tools поддерживающих OpenTelemetry. Многие платные DataDog competitors также поддерживают OpenTelemetry.

Кроме того, вы можете экспортировать OpenTelemetry метрики в Prometheus используя Grafana или другие популярные альтернативы.

Что дальше

Чтобы начать работать с Bun, используйте документацию и запустите примеры c GitHub.

Если вы уже используете database/sql, то вы можете начать использовать Bun не изменяя существующий код. Bun также поддерживает Go и SQL миграции, fixtures, хуки, и многое другое.


ссылка на оригинал статьи https://habr.com/ru/post/663332/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *