Миграция базы данных в Go при помощи пакета Migrate
В этом уроке вы узнаете, как использовать инструмент миграции (написанный на Go и довольно популярный в сообществе Go) для выполнения миграции базы данных. Во второй части вы напишете код Go для чтения данных из базы данных.
В этой статье вы собираетесь использовать PostgreSQL в качестве базы данных, но миграция совместима со многими другими базами данных.
Настройка
Установка пакета
migrate
— это инструмент командной строки, который используется для запуска миграции. Его также можно использовать программно, но в этом уроке мы собираемся использовать его через интерфейс командной строки. Существует несколько способов установки migrate
командной строки, например Brew, Scoop и т. д. Ознакомьтесь с документацией по установке, чтобы увидеть все доступные варианты.
Мы собираемся установить его с помощью go install
.
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
База данных
Для запуска примера базы данных Postgres мы предполагаем что у вас уже установлена база данных, локально или в docker контейнере.
Создание миграций
Вы собираетесь создать posts
таблицу, в которой будет два столбца: заголовок и тело.
Чтобы создать новую миграцию, выполните migrate create
команду с соответствующими параметрами.
migrate create -ext sql -dir db/migrations create_posts_table
- ext указывает расширение файла, которое будет использоваться при создании файла миграции.
- dir указывает, в каком каталоге создавать миграции.
Это создаст две миграции в db/migrations
папке, соответствующей шаблону: <timestamp>_create_posts_table.down.sql
и <timestamp>_create_posts_table.up.sql
.
- Миграция UP будет содержать sql для создания таблицы сообщений.
- Миграция ВНИЗ будет содержать sql для возврата к тому, что было сделано при миграции вверх .
SQL
В <timestamp>_create_posts_table.up.sql
файле миграции напишите sql для создания таблицы сообщений .
CREATE TABLE IF NOT EXISTS posts (title varchar, body varchar);
В <timestamp>_create_posts_table.down.sql
файле миграции напишите sql для удаления таблицы сообщений .
DROP TABLE IF EXISTS posts;
Запуск миграции
Для миграции нужен способ подключения базы данных для выполнения операторов sql. Для этого вам понадобится действительная строка подключения Postgres следующего формата:
postgres://<username>:<password>@localhost:<port>/<db_name>?sslmode=disable
Если вы используете базу данных PostgreSQL, строка подключения будет выглядеть следующим образом:
postgres://postgres:postgres@localhost:5454/postgres?sslmode=disable
Для запуска миграции используйте migrate up
команду с соответствующими параметрами.
export DB_URL='postgres://postgres:postgres@localhost:5454/postgres?sslmode=disable' # Run migrations migrate -database ${DB_URL} -path db/migrations up
Откат миграций
Чтобы откатить все миграции, т.е. выполнить все *.down.sql
файлы, используйте migration down
команду.
export DB_URL='postgres://postgres:postgres@localhost:5454/postgres?sslmode=disable' migrate -database ${DB_URL} -path db/migrations down
Написание кода
Теперь, когда у вас есть posts
таблица в базе данных, вы собираетесь написать код Go для доступа к данным. Это необязательный раздел, можете его пропустить.
Для доступа к базе данных postgres нам понадобится драйвер базы данных, давайте воспользуемся популярным пакетом pq .
go get github.com/lib/pq
Устанавливаем соединение с базой данных.
package main import ( "database/sql" "log" _ "github.com/lib/pq" ) func main() { // credentials used from the compose setup previously db, err := sql.Open("postgres", "postgres://postgres:postgres@localhost:5454/postgres?sslmode=disable") if err != nil { log.Fatal(err) } defer db.Close() if err := db.Ping(); err != nil { log.Fatal("Failed to ping db", err) } }
Создайте структуру для представления строки post
таблицы.
type Post struct { Title string Body string }
Выбор всех сообщений из базы данных и их отображение.
package main import ( "database/sql" "log" "fmt" _ "github.com/lib/pq" ) type Post struct { Title string Body string } func main() { ... // previous code of db connection setup ... // query all the posts result, err := db.Query("SELECT * FROM posts;") if err != nil { log.Fatal(err) } defer result.Close() // iterate over all the posts and print them for result.Next() { var p Post err := result.Scan(&p.Title, &p.Body) if err != nil { log.Fatal(err) } fmt.Printf("%+v\n", p) } }