Ответ
Да, я регулярно работал с миграциями баз данных. В Go-проектах наиболее популярны два подхода: использование .sql файлов и написание миграций на самом Go.
Я использовал следующие инструменты:
-
golang-migrate/migrate: Это, пожалуй, самая популярная и универсальная библиотека. Она поддерживает огромное количество баз данных и источников миграций (файлы, go-bindata, S3 и т.д.).- Подход: Используются
.sqlфайлы с определенным форматом именования, например:000001_create_users_table.up.sqlи000001_create_users_table.down.sql. - Пример
...up.sqlфайла:CREATE TABLE users ( id UUID PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) UNIQUE NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP );
- Подход: Используются
-
pressly/goose: Еще один популярный инструмент, который также делает упор на.sqlфайлы, но с другим синтаксисом внутри файла.- Подход: Используются комментарии
-- +goose Upи-- +goose Downв одном SQL-файле. -
Пример файла миграции:
-- +goose Up CREATE TABLE products ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, price NUMERIC(10, 2) NOT NULL ); -- +goose Down DROP TABLE products;
- Подход: Используются комментарии
-
Программные миграции: Иногда, если логика миграции сложна (например, требует переноса и трансформации данных), миграции пишутся на Go. Это можно сделать с помощью того же
golang-migrate/migrateили других библиотек.-
Пример запуска миграций из кода:
import "github.com/golang-migrate/migrate/v4" // ... импорты драйверов БД и источников func RunMigrations(databaseURL, migrationsPath string) error { m, err := migrate.New( "file://" + migrationsPath, // источник миграций databaseURL, // URL подключения к БД ) if err != nil { return err } // Применяем все доступные миграции "вверх" if err := m.Up(); err != nil && err != migrate.ErrNoChange { return err } return nil }
-
Выбор инструмента зависит от проекта. Для большинства CRUD-сервисов я предпочитаю golang-migrate/migrate с SQL-файлами за его простоту, наглядность и независимость от кода приложения. Программные миграции использую в редких случаях, когда требуется сложная логика преобразования данных.
Ответ 18+ 🔞
А, миграции, блядь! Ну, это ж святое, ёпта! Без них нихуя не работает, как без костылей инвалид. Я с этой хуйней регулярно сталкиваюсь, да.
В Гошных проектах, сука, обычно два лагеря: одни за чистые SQL-файлы, другие — за код на Go. Это как спор, хуй с горы или манда с ушами — у каждого свои аргументы.
Вот что я юзал, на самом деле:
-
golang-migrate/migrate— это, блядь, классика жанра, овердохуища популярная штука. Поддерживает дохуя баз данных и откуда миграции тянуть — из файлов, из памяти, с облаков, сука, наверное, даже из жопы у кенгуру.- Как работает: Ты пишешь два файлика, сука. Один — чтобы накатить (
up.sql), другой — чтобы откатить (down.sql). Имена у них, блядь, строгие, нумерованные, чтобы порядок не сбить, а то пиздец наступит. - Вот смотри, как выглядит файл наката (
...up.sql):CREATE TABLE users ( id UUID PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) UNIQUE NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP );А в
down.sqlбудетDROP TABLE users;. Всё, пизда, коротко и ясно.
- Как работает: Ты пишешь два файлика, сука. Один — чтобы накатить (
-
pressly/goose— тоже, сука, монстр известный. Тут подход чуток иной, но суть та же.- Фишка в чём: Всё в один файл запихивается, но разделяется волшебными комментариями. Типа, вот тут, блядь, мы поднимаемся (
-- +goose Up), а вот тут — откатываемся в пизду (-- +goose Down). -
Пример одной миграции в одном файле:
-- +goose Up CREATE TABLE products ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, price NUMERIC(10, 2) NOT NULL ); -- +goose Down DROP TABLE products;Удобно, ёпта, всё в одном месте. Не надо, как дурак, между двумя файлами прыгать.
- Фишка в чём: Всё в один файл запихивается, но разделяется волшебными комментариями. Типа, вот тут, блядь, мы поднимаемся (
-
А вот это, блядь, для извращенцев — миграции прямо на Go. Это когда твоя логика настолько ебнутая, что простым SQL не отделаешься. Ну там, данные перенести, преобразовать, нахуй, всё такое.
- Тут уже пишешь код, сука. Можно через ту же библиотеку
golang-migrate, но в качестве источника указать, что миграции — это Go-функции. Выглядит, конечно, мощно, но и ответственности, блядь, дохуя. -
Вот, на коленке, как это примерно из кода запускается:
import "github.com/golang-migrate/migrate/v4" // ... остальные импорты, без них нихуя func RunMigrations(databaseURL, migrationsPath string) error { m, err := migrate.New( "file://" + migrationsPath, // вот отсюда, сука, файлы тащи databaseURL, // а сюда коннекться ) if err != nil { return err // если ошибка — всё, пиздец, приехали } // И тут команда: "Всем подняться, блядь!" if err := m.Up(); err != nil && err != migrate.ErrNoChange { return err // если уже всё накатано — ок, не дергаемся } return nil }
- Тут уже пишешь код, сука. Можно через ту же библиотеку
А какой выбрать, спрашиваешь? Да похуй, в общем-то. Для 95% проектов, где просто таблицы создавать или поля добавлять — бери golang-migrate с SQL-файлами. Просто, наглядно, и от твоего кода не зависит, блядь. А вот эти программные миграции на Go — это уже для особых случаев, когда мозг выебать надо. Как говорится, волнение ебать, а терпения — ноль.