Go: GORM
Теория: Определение моделей
После подключения к базе данных GORM должен «увидеть», с какими сущностями он работает. Для этого в коде описываются модели — обычные структуры Go, которые связываются с таблицами в базе. Каждая структура играет роль чертежа, по которому GORM строит схему. Имя типа превращается в имя таблицы, поле структуры становится столбцом, а типы Go отображаются в типы СУБД. Такой подход позволяет описывать данные на языке предметной области, а преобразование в SQL доверять ORM.
GORM строит таблицы и запросы, опираясь на описание структур. По умолчанию имя структуры переводится в нижний регистр и во множественное число: тип User превращается в таблицу users, Book — в books. Поле с именем ID воспринимается как первичный ключ. Остальные поля автоматически превращаются в столбцы. Для каждого поля библиотека подбирает тип в базе: string сопоставляется с текстовым типом, целые числа — с INTEGER, время — с TIMESTAMP и аналогичными типами СУБД. Такая автоматика закрывает большинство базовых сценариев без ручного SQL.
Простейшая модель пользователя и миграция таблицы могут выглядеть так:
После запуска такого кода GORM создаёт таблицу users с колонками id, name, email и age, если её ещё не существует. Дальше та же структура используется для работы с данными. Вставка записи и чтение из базы укладываются в несколько строк:
GORM формирует INSERT и SELECT сам, подставляет значения параметров и раскладывает результат по полям структуры. Код остаётся на уровне предметной области и не опускается до текстовых запросов.
Имя таблицы по умолчанию удобно не всегда. Когда в существующей базе используются нестандартные наименования, GORM нужно подсказать правильное имя. Для этого структура может объявить метод TableName:
Теперь все операции с моделью User будут обращаться к таблице app_users. Такой приём помогает адаптировать ORM к уже существующей схеме без её переписывания.
Точнее управлять отображением полей в таблицу позволяют теги GORM. Эти теги описываются в обратных кавычках после объявления поля и задают имя столбца, тип, ограничения и ключи. Пример модели с настроенными тегами показывает, как структура управляет схемой базы:
В этом описании primaryKey делает поле ID первичным ключом. Параметр column задаёт имя столбца login вместо стандартного username. size ограничивает длину строки, unique добавляет уникальный индекс, not null запрещает пустые значения. Параметр type переопределяет тип столбца в базе, если поведение по умолчанию не подходит. Значение default определяет значение по умолчанию для Age. После AutoMigrate() GORM строит схему c учётом всех этих указаний, и необходимость писать CREATE TABLE вручную исчезает.
Связи между таблицами также управляются через теги. Внешний ключ и поведение при обновлении и удалении можно описать в самой модели. Пример с заказами показывает такую настройку:
Поле UserID хранит идентификатор пользователя и получает ограничения NOT NULL и INDEX. Поле User задаёт связь: foreignKey указывает, что для связи используется UserID, параметр references объявляет, на какое поле в структуре User идёт ссылка. А constraint описывает поведение СУБД: при обновлении ключа в таблице пользователей заказы обновятся каскадно, а при удалении пользователя ссылки в заказах превратятся в NULL. Такая конфигурация задаётся один раз в коде и затем транслируется в схему базы.
Глобальное управление именами таблиц возможно через стратегию именования. Если все таблицы должны иметь общий префикс, его удобно задать в конфигурации GORM:
После этого User связан с app_users, Order() — с app_orders и так далее. Такой подход выравнивает схему по единым правилам и помогает избежать конфликтов имён.
Отдельную роль играют служебные поля CreatedAt, UpdatedAt и DeletedAt. Большинство таблиц нуждается в информации о том, когда запись создана, когда изменена и была ли удалена логически. GORM умеет управлять такими полями автоматически, если они присутствуют в структуре. Поле CreatedAt заполняется при создании записи. UpdatedAt меняется при каждом обновлении. DeletedAt применяется для «мягкого удаления», когда запись остаётся в таблице, но помечается как скрытая.
Пример модели с временными полями и мягким удалением выглядит так:
При создании новой записи через Create() GORM автоматически заполняет CreatedAt и UpdatedAt текущим временем. При каждом обновлении через Update() или Save() поле UpdatedAt меняется, а CreatedAt остаётся неизменным. При удалении через db.Delete запись не исчезает физически: GORM устанавливает в DeletedAt текущий момент времени, а при последующих выборках с обычными методами такие строки больше не попадают в результаты. Чтобы увидеть все записи, включая помеченные как удалённые, используется Unscoped(). Для физического удаления строк с ненужными данными также применяется Unscoped() вместе с Delete().
Для удобства GORM предоставляет встроенный тип gorm.Model. Этот тип уже включает в себя стандартный набор полей: ID, CreatedAt, UpdatedAt и DeletedAt. Любая модель, встраивающая gorm.Model, автоматически получает эти поля и стандартное поведение:
В этом случае нет необходимости отдельно описывать служебные поля: GORM управляет ими по тем же правилам, что и в явном варианте. Такой подход упрощает код и делает модели однородными.
Работа с моделями в GORM становится надёжной, когда структура действительно отражает схему, а теги аккуратно задают ключи, типы и связи. Структуры выполняют роль контракта между приложением и базой, а ORM берёт на себя преобразование этого контракта в реальные таблицы и запросы. При изменении модели GORM помогает постепенно эволюционировать схему, не вынося каждый шаг в отдельный ручной SQL.



