1. Установка инструмента ent
Для установки инструмента генерации кода ent
вам необходимо выполнить следующие шаги:
Сначала убедитесь, что на вашей системе установлена среда языка Go. Затем выполните следующую команду, чтобы получить инструмент ent
:
go get -d entgo.io/ent/cmd/ent
Эта команда загрузит код инструмента ent
, но не будет немедленно его компилировать и устанавливать. Если вы хотите установить ent
в каталог $GOPATH/bin
, чтобы иметь возможность использовать его в любом месте, вам также нужно выполнить следующую команду:
go install entgo.io/ent/cmd/ent
Как только установка будет завершена, вы можете проверить, корректно ли установлен инструмент ent
, и просмотреть доступные команды и инструкции, выполнив ent -h
.
2. Инициализация схемы
2.1 Инициализация шаблона с помощью ent init
Создание нового файла схемы - первый шаг для начала использования ent
для генерации кода. Вы можете инициализировать шаблон схемы, выполнив следующую команду:
go run -mod=mod entgo.io/ent/cmd/ent new User Pet
Эта команда создаст два новых файла схемы: user.go
и pet.go
, и поместит их в каталог ent/schema
. Если каталог ent
не существует, эта команда также автоматически его создаст.
Запуск команды ent init
в корневом каталоге проекта является хорошей практикой, так как это помогает поддерживать структуру и ясность каталога проекта.
2.2 Структура файла схемы
В каталоге ent/schema
каждая схема соответствует исходному файлу на языке Go. Файлы схем - это место, где вы определяете модель базы данных, включая поля и связи (отношения).
Например, в файле user.go
вы можете определить модель пользователя, включая поля, такие как имя пользователя и возраст, и определить отношение между пользователями и питомцами. Аналогично, в файле pet.go
вы можете определить модель питомца и связанные с ним поля, такие как имя питомца, тип, и отношение между питомцами и пользователями.
Эти файлы в конечном итоге будут использоваться инструментом ent
для генерации соответствующего кода на языке Go, включая клиентский код для операций с базой данных CRUD (Create, Read, Update, Delete).
// ent/schema/user.go
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// User определяет схему для сущности User.
type User struct {
ent.Schema
}
// Метод Fields используется для определения полей сущности.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name").Unique(),
field.Int("age").Positive(),
}
}
// Метод Edges используется для определения ассоциаций сущности.
func (User) Edges() []ent.Edge {
// Ассоциации будут подробно объяснены в следующем разделе.
}
Файлы схем ent
используют типы и функции языка Go для объявления структуры модели базы данных, включая поля и отношения между моделями, и используют API, предоставляемый фреймворком ent
, для определения этих структур. Такой подход делает ent
очень интуитивно понятным и легким в расширении, используя при этом сильную типизацию языка Go.
3.1 Запуск генерации кода
Запуск ent generate
для генерации кода является важным шагом в фреймворке ent
. С помощью этой команды ent
сгенерирует соответствующие файлы кода на языке Go на основе определенных схем, упрощая последующую разработку. Команда для выполнения генерации кода проста:
go generate ./ent
Вышеуказанную команду необходимо выполнять в корневом каталоге проекта. При вызове go generate
он будет искать все файлы на языке Go, содержащие определенные аннотации, и выполнять команды, указанные в аннотациях. В нашем примере данная команда указывает генератор кода для ent
.
Убедитесь, что инициализация схемы и необходимое добавление полей завершены перед выполнением. Только в этом случае сгенерированный код будет содержать все необходимые части.
3.2 Понимание сгенерированных кодовых ресурсов
Сгенерированные кодовые ресурсы содержат несколько компонентов, каждый из которых обладает различными функциями:
-
Объекты Client и Tx: Используются для взаимодействия с графом данных. Client предоставляет методы для создания транзакций (Tx) или непосредственного выполнения операций с базой данных.
-
Генераторы CRUD: Для каждого типа схемы генерируются средства создания, чтения, обновления и удаления, упрощая логику операций соответствующей сущности.
-
Объект сущности (Go struct): Для каждого типа в схеме генерируются соответствующие Go-структуры, отображающие эти структуры на таблицы в базе данных.
-
Пакеты констант и предикатов: Содержат константы и предикаты для взаимодействия с генераторами.
-
Пакет миграции: Пакет для миграции базы данных, подходящий для диалектов SQL.
-
Пакет хуков: Предоставляет функциональность для добавления промежуточного ПО изменений, позволяя выполнить пользовательскую логику перед или после создания, обновления или удаления сущностей.
При изучении сгенерированного кода вы можете получить более глубокое понимание того, как фреймворк ent
автоматизирует доступ к данным для ваших схем.
4. Опции генерации кода
Команда ent generate
поддерживает различные опции для настройки процесса генерации кода. Вы можете запросить все поддерживаемые опции генерации с помощью следующей команды:
ent generate -h
Вот некоторые часто используемые опции командной строки:
-
--feature strings
: Расширяет генерацию кода, добавляя дополнительную функциональность. -
--header string
: Переопределяет файл заголовка генерации кода. -
--storage string
: Указывает используемый драйвер хранения в процессе генерации кода, по умолчанию используется "sql". -
--target string
: Указывает целевой каталог для генерации кода. -
--template strings
: Выполняет дополнительные шаблоны Go. Поддерживает файл, каталог и шаблон пути, например:--template file="путь/к/файлу.tmpl"
.
Эти опции позволяют разработчикам настраивать процесс генерации своего кода в соответствии с различными потребностями и предпочтениями.
5. Настройка опции хранения
ent
поддерживает генерацию кодовых ресурсов как для диалектов SQL, так и для Gremlin, по умолчанию используется диалект SQL. Если проекту необходимо подключиться к базе данных Gremlin, необходимо настроить соответствующий диалект базы данных. Ниже приведено, как указать опции хранения:
ent generate --storage gremlin ./ent/schema
Эта команда указывает ent
использовать диалект Gremlin при генерации кода. Это гарантирует, что сгенерированные ресурсы будут адаптированы к требованиям базы данных Gremlin, обеспечивая совместимость с конкретной графовой базой данных.
6. Расширенное использование: Пакет entc
6.1 Использование entc
как пакета в проекте
entc
- это основной пакет, используемый для генерации кода в фреймворке ent
. Помимо инструмента командной строки, entc
также может быть интегрирован в проект как пакет, позволяя разработчикам контролировать и настраивать процесс генерации кода непосредственно в коде.
Чтобы использовать entc
как пакет в проекте, вам необходимо создать файл с именем entc.go
и добавить следующее содержимое в файл:
// +build ignore
package main
import (
"log"
"entgo.io/ent/entc"
"entgo.io/ent/entc/gen"
)
func main() {
if err := entc.Generate("./schema", &gen.Config{}); err != nil {
log.Fatal("running ent codegen:", err)
}
}
При использовании этого подхода вы можете изменять структуру gen.Config
внутри функции main
, чтобы применить различные опции конфигурации. Вызывая функцию entc.Generate
по мере необходимости, вы гибко управляете процессом генерации кода.
6.2 Подробная настройка entc
entc
предоставляет обширные параметры настройки, позволяя разработчикам настраивать сгенерированный код. Например, можно настроить пользовательские хуки для проверки или изменения сгенерированного кода, а также внедрять внешние зависимости с использованием инъекции зависимостей.
В следующем примере показано, как создать пользовательские хуки для функции entc.Generate
:
func main() {
err := entc.Generate("./schema", &gen.Config{
Hooks: []gen.Hook{
HookFunction,
},
})
if err != nil {
log.Fatalf("Ошибка при запуске генерации ent code: %v", err)
}
}
// HookFunction - пользовательская функция-хук
func HookFunction(next gen.Generator) gen.Generator {
return gen.GenerateFunc(func(g *gen.Graph) error {
// Можно обработать здесь представленный графический режим g
// Например, проверить наличие полей или изменить структуру
return next.Generate(g)
})
}
Кроме того, внешние зависимости можно добавлять с помощью entc.Dependency
:
func main() {
opts := []entc.Option{
entc.Dependency(
entc.DependencyType(&http.Client{}),
),
entc.Dependency(
entc.DependencyName("Writer"),
entc.DependencyTypeInfo(&field.TypeInfo{
Ident: "io.Writer",
PkgPath: "io",
}),
),
}
if err := entc.Generate("./схема", &gen.Config{}, opts...); err != nil {
log.Fatalf("Ошибка при запуске генерации ent code: %v", err)
}
}
В этом примере мы внедряем http.Client
и io.Writer
в качестве зависимостей в сгенерированные объекты клиента.
7. Вывод описания схемы
В фреймворке ent
команду ent describe
можно использовать для вывода описания схемы в графическом формате. Это поможет разработчикам быстро понять существующие сущности и отношения.
Для получения описания схемы выполните следующую команду:
go run -mod=mod entgo.io/ent/cmd/ent describe ./ent/schema
Вышеуказанная команда выведет таблицу, аналогичную следующей, отображая информацию, такую как поля, типы, отношения и т. д. для каждой сущности:
Пользователь:
+-------+---------+--------+-----------+ ...
| Поле | Тип | Уникально | Опционально | ...
+-------+---------+--------+-----------+ ...
| id | int | false | false | ...
| name | string | true | false | ...
+-------+---------+--------+-----------+ ...
+-------+--------+---------+-----------+ ...
| Ребро | Тип | Обратное | Отношение | ...
+-------+--------+---------+-----------+ ...
| pets | Pet | false | O2M | ...
+-------+--------+---------+-----------+ ...
8. Хуки генерации кода
8.1 Концепция хуков
Хуки - это промежуточные функции, которые можно вставлять в процесс генерации кода ent
, позволяя вставлять пользовательскую логику до и после генерации кода. Хуки могут использоваться для манипуляции абстрактным синтаксическим деревом (AST) сгенерированного кода, выполнения проверок или добавления дополнительных фрагментов кода.
8.2 Пример использования хуков
Вот пример использования хука для обеспечения того, что все поля содержат определенный структурный тег (например, json
):
func main() {
err := entc.Generate("./schema", &gen.Config{
Hooks: []gen.Hook{
EnsureStructTag("json"),
},
})
if err != nil {
log.Fatalf("Ошибка при запуске генерации ent code: %v", err)
}
}
// EnsureStructTag обеспечивает, что все поля в графе содержат определенный структурный тег
func EnsureStructTag(name string) gen.Hook {
return func(next gen.Generator) gen.Generator {
return gen.GenerateFunc(func(g *gen.Graph) error {
for _, node := range g.Nodes {
for _, field := range node.Fields {
tag := reflect.StructTag(field.StructTag)
if _, ok := tag.Lookup(name); !ok {
return fmt.Errorf("Структурный тег %q отсутствует для поля %s.%s", name, node.Name, field.Name)
}
}
}
return next.Generate(g)
})
}
}
В этом примере перед генерацией кода функция EnsureStructTag
проверяет каждое поле на наличие тега json
. Если поле не содержит этот тег, то процесс генерации кода завершится ошибкой. Это эффективный способ поддерживать чистоту и последовательность кода.