Найти тему
Wissance

GORM LevelUp (переводим на новый уровень, v2)

Оглавление
Разница между Gorm V2 и V1 это как разница между могучей горгоной и обычной
Разница между Gorm V2 и V1 это как разница между могучей горгоной и обычной

Многие используют данную ORM для работы с базами данных с проекцией таблиц на структуры данных. Работать с ORM удобно, лично я в большинстве проектов использую ORM и только в небольшом числе проектов использую сырой SQL и работу с драйвером напрямую. Сегодня речь пойдет о GORM снова, снова означает, что я уже ранее писал статью о разного рода проблемах при использовании этой ОРМ и как их можно обойти. Эта статья была рассчитана на версию 1 GORM, сегодня мы будем лэвэлапить проект и обновлять версию GORM в нем. Прежде чем начать огромная просьба подписаться на Zen-канал и наш twitter.

Про версии GORM

Сначала вро версии GORM и возникает резонный вопрос: "а зачем нужно обновлять если все работает?" На самом деле далеко НЕ ВСЕ работает, для этого приходится "костылить" смотри ссылку на мою статью выше.

В Golang дистрибьюция пакетов осуществляется через git, большинство пакетов распространяется через github. Версия пакетов должна соответсвовать опредленному формату. Однако в этой статье я не указал, что значение основной версии может быть либо 0, либо 1, иначе для импорта нужно использовать что-то типа: github.com/wissance/gwuu/v2 (v3, v4 и т.п.). Однако, автор GORM'а по каким-то причинам не стал использовать эту схему и перешел на другой домен (gorm.io). Из-за этого я долгое время не мог понять почему в туториалах часть примеров работает, а часть нет, дело все в том, что здесь все примеры идут в перемешку (документация общая и для v1 и для v2) и ничего не объясняется про версии. Если хочешь полный туториал по GORM, то ставь лайк и подписку на канал (сделаем туториал при 500 подписчиках и 200 лайках к этой статье).

Поэтому резюмируя отметим следующее:

  • gorm v1 - github.com/jinzhu/gorm
  • gorm v2 - gorm.io/gorm

В чем профит переезда с v1 на v2 ? Попробуем разобраться детально.

Разница между Gorm v1 и v2

Первый и самый пожалуй важный профит - в gorm v2 корректно происходит закрытие соединения с БД через вызов метода Close(). Есть разные подходы к работе с внешними ресурсами, которыми не управляет приложение:

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

При использовании короткоживущих соединений в Web-приложениях "per request" с Postgres я получил следующую проблему, в какой-то момент приложение перестало соединяться с БД, оказалось, что GORM подвешивает сессии Postgres и все. Нужно перезапускать postgres

Вот, что происходит с сессиями Posgtres если использовать gorm v1.
Вот, что происходит с сессиями Posgtres если использовать gorm v1.

Gorm v2 корректно работает с сессиями и без проблем может закрыть при этом мы не видем подвешенных сессий.

Второй также немаловажный момент - gorm v2 умеет нормально создавать новое значение для первичного ключа, ранее приходилось использовать костыль и получать MAX(Id) и самостоятельно задавать новое значение для первичного ключа в виде MAX(Id) + 1 и использовать его в Id при создании объекта. Т.е. в gorm v1 мы ранее использовали функцию GetNextTableId из gwuu (кстати в этом проекте есть некоторые полезные функции для работы как с gorm, так и для web-приложений, поэтому поставь звездочку бро).

Третий момент - описание связей между таблицами, в v1 связи one-2-many, many-2-many создавались в процессе миграций БД:

// Auto migrate & set up relations

func PrepareDb(db *gorm.DB) { // Auto migrate
db.AutoMigrate(&RuntimeType{}, &Runtime{}, &EnvironmentType{},
&Environment{}, &EnvironmentValue{}, &Function{}, &Group{}, &Tag{})
db.Model(Runtime{}).AddForeignKey("runtime_type_id",
"runtime_types(id)", "CASCADE",
"CASCADE")
db.Model(Environment{}).AddForeignKey("type_id",
"environment_types(id)",
"CASCADE", "CASCADE")
db.Model(EnvironmentValue{}).AddForeignKey("environment_id",
"environments(id)",
"CASCADE",
"CASCADE")
db.Model(EnvironmentValue{}).AddForeignKey("function_id",
"functions(id)",
"CASCADE",
"CASCADE")
db.Model(Function{}).AddForeignKey("runtime_id", "runtimes(id)",
"CASCADE", "CASCADE") }
db.Model(Function{}).AddForeignKey("group_id", "groups(id)", "SET
NULL", "CASCADE")
db.Table("functions").AddForeignKey("function_id", "functions(id)",
"CASCADE", "CASCADE")
db.Table("function_tags").AddForeignKey("tag_id", "tags(id)", "CASCADE",
"CASCADE")

Как видно из примера выше добавление внешних ключей foreign_key происходит через вызов отдельных методов Gorm - AddForeignKey. Еще хуже обстоит дело с mant-2-many в Gorm v1, для создания таких связей НЕОБХОДИМО напрямую обращаться к таблицам через db.Table. Gorm v2 решает и эту проблему. Теперь для этого достаточно лишь добавить аннотации в модель:

type Function struct {
gorm.Model
// ID uint `gorm:"primary_key; AUTO_INCREMENT"`
Name string `gorm:"type:varchar(255);not null;unique;"`
DisplayName string `gorm:"type:varchar(255);not null;"`
FunctionCodeFile string `gorm:"type:varchar(512);not null;"`
Runtime Runtime `gorm:"foreignkey:RuntimeID;"`
RuntimeID uint `gorm:"not null;"`
Tags []Tag `gorm:"many2many:function_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
GroupId sql.NullInt32 `gorm:"default:null;"`
Group Group `gorm:"foreignkey:GroupId;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
Version string `gorm:"type:varchar(32);not null;default:'';"`
Timeout int `gorm:"type:int;not null;default:0;"`
}

Это пожалуй одни из наиболее важных изменений (также меняется синтаксис функций транзакции, убраны некоторые функции и т.п.) Как мне кажется, переход на v2 оправдан, т.к. он устраняет кучу проблем, которые нужно было решать при использовании v1. Коротенький пример простой модели данных и работу с ней с gorm v2 можно посмотреть в этом модульном тесте.

Заключение

Я не могу сказать, что являюсь фанатом использование всего наиболее свежего и нового, на мой взгляд это оправдано только тогда, когда дает существенные преимущеста и переход gorm c v1 на v2 это как раз такой случай! Спасибо, что дочитал бро.