1. Основы модели и поля
1.1. Введение в Определение Модели
В ORM-фреймворке модель используется для описания соответствия между типами сущностей в приложении и таблицами базы данных. Модель определяет свойства и отношения сущности, а также связанные с ними конфигурации, специфические для базы данных. В рамках фреймворка ent модели обычно используются для описания типов сущностей в виде графа, таких как User
или Group
.
Определения модели обычно включают описания полей (или свойств) сущности и рёбер (или отношений), а также некоторые специфические для базы данных параметры. Эти описания могут помочь нам определить структуру, свойства и отношения сущности, и могут быть использованы для генерации соответствующей структуры таблицы базы данных на основе модели.
1.2. Обзор Полей
Поля представляют собой часть модели, отображающую свойства сущности. Они определяют свойства сущности, такие как имя, возраст, дата и т. д. В фреймворке ent типы полей включают различные базовые типы данных, такие как целые числа, строки, логические значения, даты и различные специфические для SQL типы, такие как UUID, []byte, JSON и др.
В таблице ниже приведены типы полей, поддерживаемые фреймворком ent:
Тип | Описание |
---|---|
int | Целочисленный тип |
uint8 | Беззнаковый 8-битный целочисленный тип |
float64 | Тип с плавающей запятой |
bool | Логический тип |
string | Строковый тип |
time.Time | Тип времени |
UUID | Тип UUID |
[]byte | Тип массива байтов (только для SQL) |
JSON | Тип JSON (только для SQL) |
Enum | Тип Enum (только для SQL) |
Другие | Другие типы (например, диапазон для Postgres) |
2. Подробности Свойств Поля
2.1. Типы данных
Тип данных атрибута или поля в модели сущности определяет форму данных, которые могут быть сохранены. Это важная часть определения модели в фреймворке ent. Ниже приведены некоторые часто используемые типы данных в фреймворке ent
.
import (
"time"
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// Схема пользователя.
type User struct {
ent.Schema
}
// Поля пользователя.
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age"), // Целочисленный тип
field.String("name"), // Строковый тип
field.Bool("active"), // Логический тип
field.Float("score"), // Тип с плавающей запятой
field.Time("created_at"), // Тип метки времени
}
}
-
int
: Представляет целочисленные значения, которые могут бытьint8
,int16
,int32
,int64
и т. д. -
string
: Представляет строковые данные. -
bool
: Представляет логические значения, обычно используется в качестве флагов. -
float64
: Представляет числа с плавающей запятой, также можно использоватьfloat32
. -
time.Time
: Представляет время, обычно используется для меток времени или дат.
Эти типы полей будут сопоставлены с соответствующими типами, поддерживаемыми базовой базой данных. Кроме того, ent
поддерживает более сложные типы, такие как UUID
, JSON
, перечисления (Enum
) и поддержку специальных типов баз данных, таких как []byte
(только для SQL) и Other
(только для SQL).
2.2. Значения по умолчанию
Поля могут быть настроены со значениями по умолчанию. Если соответствующее значение не указано при создании сущности, будет использовано значение по умолчанию. Значение по умолчанию может быть фиксированным или динамически сгенерированным значением из функции. Для установки статического значения по умолчанию используйте метод .Default
, а для установки динамически сгенерированного значения по умолчанию используйте .DefaultFunc
.
// Схема пользователя.
func (User) Fields() []ent.Field {
return []ent.Field{
field.Time("created_at").
Default(time.Now), // Фиксированное значение по умолчанию time.Now
field.String("role").
Default("user"), // Константное строковое значение
field.Float("score").
DefaultFunc(func() float64 {
return 10.0 // Значение по умолчанию, сгенерированное функцией
}),
}
}
2.3. Опциональность поля и нулевые значения
По умолчанию поля обязательны. Чтобы объявить опциональное поле, используйте метод .Optional()
. Опциональные поля будут объявлены как поля, допускающие значение null
в базе данных. Опция Nillable
позволяет явно устанавливать поля в значение nil
, различая нулевое значение поля и неустановленное состояние.
// Схема пользователя.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("nickname").Optional(), // Опциональное поле не обязательно
field.Int("age").Optional().Nillable(), // Поле, допускающее значение 'nil'
}
}
При использовании определенной модели выше, поле age
может принимать как значения nil
, чтобы указать неустановленное состояние, так и ненулевые нулевые значения.
2.4. Уникальность поля
Уникальные поля гарантируют отсутствие дублирующихся значений в таблице базы данных. Используйте метод Unique()
, чтобы определить уникальное поле. При обеспечении целостности данных в качестве критического требования, такого как для электронной почты или имени пользователя пользователей, следует использовать уникальные поля.
// Схема пользователя.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("email").Unique(), // Уникальное поле для избежания дублирования адресов электронной почты
}
}
Это создаст уникальное ограничение в базе данных, чтобы предотвратить вставку дублирующихся значений.
2.5. Индексирование поля
Индексирование поля используется для улучшения производительности запросов к базе данных, особенно в больших базах данных. Во фреймворке ent
метод .Indexes()
можно использовать для создания индексов.
import "entgo.io/ent/schema/index"
// Схема пользователя.
func (User) Indexes() []ent.Index {
return []ent.Index{
index.Fields("email"), // Создание индекса для поля 'email'
index.Fields("name", "age").Unique(), // Создание уникального составного индекса
}
}
Индексы могут использоваться для часто запрашиваемых полей, но важно отметить, что слишком много индексов может привести к уменьшению производительности операций записи. Поэтому решение о создании индексов должно быть сбалансировано в зависимости от реальных обстоятельств.
2.6. Пользовательские теги
Во фреймворке ent
вы можете использовать метод StructTag
для добавления пользовательских тегов к сгенерированным структурным полям сущности. Эти теги очень полезны для реализации операций, таких как кодирование JSON и XML. В приведенном ниже примере мы добавим пользовательские теги JSON и XML для поля name
.
// Поля пользователя.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name").
// Добавление пользовательских тегов с помощью метода StructTag
// Здесь установим тег JSON для поля name как 'username' и игнорирование его, если поле пустое (omitempty)
// Также установим тег XML для кодирования в 'name'
StructTag(`json:"username,omitempty" xml:"name"`),
}
}
При кодировании с помощью JSON или XML опция omitempty
указывает, что если поле name
пустое, то это поле будет исключено из результата кодирования. Это очень полезно для уменьшения размера тела ответа при написании API.
Это также демонстрирует, как установить несколько тегов для одного поля одновременно. Теги JSON используют ключ json
, теги XML используют ключ xml
, и они разделяются пробелами. Эти теги будут использоваться библиотечными функциями, такими как encoding/json
и encoding/xml
при анализе структуры для кодирования или декодирования.
3. Проверка поля и ограничения
Проверка поля является важным аспектом проектирования базы данных для обеспечения целостности и правильности данных в модели сущности.
3.1. Встроенные валидаторы
Фреймворк предоставляет серию встроенных валидаторов для выполнения общих проверок правильности данных для различных типов полей. Использование этих встроенных валидаторов может упростить процесс разработки и быстро определить допустимые диапазоны или форматы данных для полей.
Вот несколько примеров встроенных валидаторов полей:
- Валидаторы для числовых типов:
-
Positive()
: Проверяет, является ли значение поля положительным числом. -
Negative()
: Проверяет, является ли значение поля отрицательным числом. -
NonNegative()
: Проверяет, является ли значение поля неотрицательным числом. -
Min(i)
: Проверяет, является ли значение поля больше заданного минимального значенияi
. -
Max(i)
: Проверяет, является ли значение поля меньше заданного максимального значенияi
.
-
- Валидаторы для типа
string
:-
MinLen(i)
: Проверяет минимальную длину строки. -
MaxLen(i)
: Проверяет максимальную длину строки. -
Match(regexp.Regexp)
: Проверяет, соответствует ли строка заданному регулярному выражению. -
NotEmpty
: Проверяет, что строка не пуста.
-
Давайте рассмотрим практический пример кода. В этом примере создается модель User
, которая включает поле целочисленного типа age
и поле email
с фиксированным форматом:
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age").
Positive(),
field.String("email").
Match(regexp.MustCompile(`^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$`)),
}
}
3.2. Пользовательские валидаторы
Хотя встроенные валидаторы могут обрабатывать множество общих требований к проверке, иногда вам может потребоваться более сложная логика проверки. В таких случаях вы можете написать пользовательские валидаторы, чтобы соответствовать определенным бизнес-правилам.
Пользовательский валидатор представляет собой функцию, которая принимает значение поля и возвращает error
. Если возвращенный error
не пуст, это указывает на неудачу проверки. Общий формат пользовательского валидатора выглядит следующим образом:
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("phone").
Validate(func(s string) error {
// Проверяем, соответствует ли номер телефона ожидаемому формату
matched, _ := regexp.MatchString(`^\+?[1-9]\d{1,14}$`, s)
if !matched {
return errors.New("Неверный формат номера телефона")
}
return nil
}),
}
}
Как показано выше, мы создали пользовательский валидатор для проверки формата номера телефона.
3.3. Ограничения
Ограничения - это правила, обеспечивающие определенные требования к объекту базы данных. Они могут использоваться для обеспечения корректности и последовательности данных, таких как предотвращение ввода недопустимых данных или определение связей данных.
Общие ограничения базы данных включают:
- Ограничение первичного ключа: Обеспечивает уникальность каждой записи в таблице.
- Ограничение уникальности: Гарантирует, что значение столбца или комбинация столбцов является уникальной в таблице.
- Ограничение внешнего ключа: Определяет отношения между таблицами, обеспечивая целостность ссылочной целостности.
- Ограничение проверки: Гарантирует, что значение поля соответствует определенному условию.
В модели сущности вы можете определить ограничения для поддержания целостности данных следующим образом:
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("username").
Unique(), // Ограничение уникальности для обеспечения уникальности имени пользователя в таблице.
field.String("email").
Unique(), // Уникальное ограничение для обеспечения уникальности электронной почты в таблице.
}
}
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("friends", User.Type).
Unique(), // Ограничение внешнего ключа, создание уникального отношения к другому пользователю.
}
}
В заключение, проверка полей и ограничения имеют важное значение для обеспечения качества данных и избежания непредвиденных ошибок данных. Использование инструментов, предоставляемых фреймворком ent
, может упростить и обеспечить более надежный процесс.