GORM/GEN
Обзор
Более безопасный ORM на основе GORM, ориентированный на удобство разработки.
Содержание
Установка
Инструкции по установке и использованию пакета можно найти в документации.
Быстрый старт
Для начала работы с пакетом выполните следующие шаги:
Проектная директория
Структура проектной директории может выглядеть следующим образом:
project_directory
├── main.go
└── model
└── user.gen.go
В файле main.go будет находиться основной код приложения, а в файле user.gen.go — сгенерированная модель данных.
Примеры использования API
API пакета предоставляет различные функции для работы с данными. В этом разделе приведены примеры использования некоторых из них.
Генератор
Генератор кода позволяет создавать модели данных на основе структуры базы данных. Для этого необходимо выполнить следующие действия:
Пример создания модели данных для таблицы users:
type User struct {
ID int64
Name string
Email string
Password string
}
// Генерируем модель данных
userModel := gen.Generate("users")
После выполнения этих действий будет создан файл user.gen.go с моделью данных для таблицы users.
Создание поля
Функция CreateField позволяет создать поле в модели данных. Она принимает следующие аргументы:
Пример создания поля ID в модели данных:
userModel.CreateField("ID", int64Type, false, 0)
CRUD API
CRUD-операции позволяют выполнять основные операции с данными: создание, чтение, обновление и удаление. Пакет предоставляет следующие функции для выполнения этих операций:
Эти функции принимают в качестве аргументов модель данных и параметры операции. Пример использования функции Create для создания новой записи в таблице users:
err := userModel.Create(&User{
Name: "John Doe",
Email: "john@doe.com",
Password: "password",
})
if err != nil {
fmt.Println(err)
return
}
Ассоциации
Пакет также предоставляет функции для работы с ассоциациями между моделями данных. Эти функции позволяют связывать модели данных друг с другом и выполнять операции над связанными записями.
Отношение
Отношение между моделями данных устанавливается с помощью функции Relate. Эта функция принимает в качестве аргументов две модели данных и параметры отношения. Пример установления отношения «один ко многим» между моделями User и Post:
postModel.Relate(userModel, "author", "UserID")
Этот код устанавливает отношение «автор» между моделью User и таблицей posts в базе данных. Поле author в таблице posts будет ссылаться на поле UserID в модели User.
Операция
Операции над связанными записями выполняются с помощью функций FindAssociations, AppendAssociations и Replace. Эти функции принимают в качестве аргументов модель данных, операцию и параметры операции. Ассоциации #replace-associations
Предварительная загрузка #preloading
Обновление #update
Удаление #delete
DIY method #diy-method
Method interface #method-interface
If
clause #if-clauseWhere
clause #where-clauseSet
clause #set-clauseSmart select fields
Advanced Topics #advanced-topics
Binary
Maintainers
Contributing
License
Чтобы установить пакет Gen, вам необходимо сначала установить Go и настроить рабочее пространство Go.
go get -u gorm.io/gen
import "gorm.io/gen"
Внимание: все варианты использования в этом документе генерируются в режиме WithContext
. А если вы генерируете код в режиме WithoutContext
, пожалуйста, удалите WithContext(ctx)
перед вызовом любого метода запроса, это поможет вам сделать код более лаконичным.
# предположим, что следующий код находится в файле generate.go
$ cat generate.go
package main
import "gorm.io/gen"
// сгенерируйте код
func main() {
// укажите выходной каталог (по умолчанию: "./query")
// ### если вы хотите запросить без ограничения контекста, установите режим gen.WithoutContext ###
g := gen.NewGenerator(gen.Config{
OutPath: "../dal/query",
/* Mode: gen.WithoutContext|gen.WithDefaultQuery*/
//если вы хотите, чтобы свойство генерации поля с нулевым значением было типом указателя, установите FieldNullable true
/* FieldNullable: true,*/
//если вы хотите генерировать теги индекса из базы данных, установите FieldWithIndexTag true
/* FieldWithIndexTag: true,*/
//если вы хотите генерировать теги типа из базы данных, установите FieldWithTypeTag true
/* FieldWithTypeTag: true,*/
//если вам нужны модульные тесты для кода запроса, установите WithUnitTest true
/* WithUnitTest: true, */
})
// повторно используйте соединение с базой данных в Project или создайте здесь соединение
// если вы хотите использовать GenerateModel/GenerateModelAs, UseDB является обязательным, иначе произойдет паника
// db, _ := gorm.Open(mysql.Open("root:@(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=True&loc=Local"))
g.UseDB(db)
// примените основные операции crud api к структурам или моделям таблиц, которые указаны именем таблицы с функцией
// GenerateModel/GenerateModelAs. И генератор сгенерирует код моделей таблиц при вызове Excute.
g.ApplyBasic(model.User{}, g.GenerateModel("company"), g.GenerateModelAs("people", "Person", gen.FieldIgnore("address")))
// применить diy интерфейсы к структурам или табличным моделям
g.ApplyInterface(func(method model.Method) {}, model.User{}, g.GenerateModel("company"))
// выполнить действие генерации кода
g.Execute() ### CRUD API
Здесь представлен базовый тип структуры `user` и тип структуры `DB`.
```go
// сгенерированный код
// сгенерированный код
// сгенерированный код
package query
import "gorm.io/gen"
// struct map to table `users`
type user struct {
gen.DO
ID field.Uint
Name field.String
Age field.Int
Address field.Field
Birthday field.Time
}
// struct collection
type DB struct {
db *gorm.DB
User *user
}
// u refer to query.user
user := model.User{Name: "Modi", Age: 18, Birthday: time.Now()}
u := query.Use(db).User
err := u.WithContext(ctx).Create(&user) // передать указатель данных в Create
err // возвращает ошибку
Создайте запись и присвойте значение указанным полям.
u := query.Use(db).User
u.WithContext(ctx).Select(u.Name, u.Age).Create(&user)
// INSERT INTO `users` (`name`,`age`) VALUES ("modi", 18)
Создайте запись и игнорируйте значения для полей, переданных для пропуска.
u := query.Use(db).User
u.WithContext(ctx).Omit(u.Name, u.Age).Create(&user)
// INSERT INTO `users` (`Address`, `Birthday`) VALUES ("2021-08-17 20:54:12.000", 18)
Чтобы эффективно вставить большое количество записей, передайте срез методу Create
. GORM сгенерирует один оператор SQL для вставки всех данных и заполнит значения первичных ключей.
var users = []model.User{{Name: "modi"}, {Name: "zhangqiang"}, {Name: "songyuan"}}
query.Use(db).User.WithContext(ctx).Create(&users)
for _, user := range users {
user.ID // 1,2,3
}
Вы можете указать размер пакета при создании с помощью CreateInBatches
, например:
var users = []User{{Name: "modi_1"}, ...., {Name: "modi_10000"}}
// размер пакета 100
query.Use(db).User.WithContext(ctx).CreateInBatches(users, 100)
Это будет работать, если вы установите CreateBatchSize
в gorm.Config
/ gorm.Session
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
CreateBatchSize: 1000,
})
// ИЛИ
db = db.Session(&gorm.Session{CreateBatchSize: 1000})
u := query.NewUser(db)
var users = []User{{Name: "modi_1"}, ...., {Name: "modi_5000"}}
u.WithContext(ctx).Create(&users)
// INSERT INTO users xxx (5 пакетов)
Сгенерированный код предоставляет методы First
, Take
, Last
для извлечения одного объекта из базы данных, он добавляет условие LIMIT 1
при запросе к базе данных и вернёт ошибку ErrRecordNotFound
, если запись не найдена.
u := query.Use(db).User
// Получить первую запись, упорядоченную по первичному ключу
user, err := u.WithContext(ctx).First()
// SELECT * FROM users ORDER BY id LIMIT 1;
// Получить одну запись без указания порядка
user, err := u.WithContext(ctx).Take()
// SELECT * FROM users LIMIT 1;
// Получить последнюю запись, упорядоченную по первичному ключу по убыванию
user, err := u.WithContext(ctx).Last()
// SELECT * FROM users ORDER BY id DESC LIMIT 1;
// проверить ошибку ErrRecordNotFound
errors.Is(err, gorm.ErrRecordNotFound)
u := query.Use(db).User
user, err := u.WithContext(ctx).Where(u.ID.Eq(10)).First()
// SELECT * FROM users WHERE id = 10;
users, err := u.WithContext(ctx).Where(u.ID.In(1,2,3)).Find()
// SELECT * FROM users WHERE id IN (1,2,3);
Если первичный ключ является строкой (например, как uuid), запрос будет записан следующим образом:
user, err := u.WithContext(ctx).Where(u.ID.Eq("1b74413f-f3b8-409f-ac47-e8c062e3472a")).First()
// SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";
u := query.Use(db).User
// Получить все записи
users, err := u.WithContext(ctx).Find()
// SELECT * FROM users;
u := query.Use(db).User
// Получить первое совпадение **Limit & Offset**
Limit указывает максимальное количество записей для извлечения.
Offset указывает количество записей, которые нужно пропустить перед началом возврата записей.
```go
u := query.Use(db).User
urers, err := u.WithContext(ctx).Limit(3).Find()
// SELECT * FROM users LIMIT 3;
// Отменить условие limit с помощью -1
users, err := u.WithContext(ctx).Limit(10).Limit(-1).Find()
// SELECT * FROM users;
users, err := u.WithContext(ctx).Offset(3).Find()
// SELECT * FROM users OFFSET 3;
users, err := u.WithContext(ctx).Limit(10).Offset(5).Find()
// SELECT * FROM users OFFSET 5 LIMIT 10;
// Отменить условие offset с помощью -1
users, err := u.WithContext(ctx).Offset(10).Offset(-1).Find()
// SELECT * FROM users;
Group By & Having
u := query.Use(db).User
type Result struct {
Date time.Time
Total int
}
var result Result
err := u.WithContext(ctx).Select(u.Name, u.Age.Sum().As("total")).Where(u.Name.Like("%modi%")).Group(u.Name).Scan(&result)
// SELECT name, sum(age) as total FROM `users` WHERE name LIKE "%modi%" GROUP BY `name`
err := u.WithContext(ctx).Select(u.Name, u.Age.Sum().As("total")).Group(u.Name).Having(u.Name.Eq("group")).Scan(&result)
// SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
rows, err := u.WithContext(ctx).Select(u.Birthday.As("date"), u.Age.Sum().As("total")).Group(u.Birthday).Rows()
for rows.Next() {
...
}
o := query.Use(db).Order
rows, err := o.WithContext(ctx).Select(o.CreateAt.Date().As("date"), o.Amount.Sum().As("total")).Group(o.CreateAt.Date()).Having(u.Amount.Sum().Gt(100)).Rows()
for rows.Next() {
...
}
var results []Result
o.WithContext(ctx).Select(o.CreateAt.Date().As("date"), o.WithContext(ctx).Amount.Sum().As("total")).Group(o.CreateAt.Date()).Having(u.Amount.Sum().Gt(100)).Scan(&results)
Distinct
Выбор различных значений из модели.
u := query.Use(db).User
users, err := u.WithContext(ctx).Distinct(u.Name, u.Age).Order(u.Name, u.Age.Desc()).Find()
Distinct работает с Pluck и Count.
Joins
Указание условий соединения.
u := query.Use(db).User
e := query.Use(db).Email
c := query.Use(db).CreditCard
type Result struct {
Name string
Email string
}
var result Result
err := u.WithContext(ctx).Select(u.Name, e.Email).LeftJoin(e, e.UserID.EqCol(u.ID)).Scan(&result)
// SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id
rows, err := u.WithContext(ctx).Select(u.Name, e.Email).LeftJoin(e, e.UserID.EqCol(u.ID)).Rows()
for rows.Next() {
...
}
var results []Result
err := u.WithContext(ctx).Select(u.Name, e.Email).LeftJoin(e, e.UserID.EqCol(u.ID)).Scan(&results)
// Несколько соединений с параметром
users := u.WithContext(ctx).Join(e, e.UserID.EqCol(u.id), e.Email.Eq("modi@example.org")).Join(c, c.UserID.EqCol(u.ID)).Where(c.Number.Eq("411111111111")).Find()
SubQuery
Подзапрос может быть вложен в запрос, GEN может генерировать подзапрос при использовании объекта Dao в качестве параметра.
o := query.Use(db).Order
u := query.Use(db).User
orders, err := o.WithContext(ctx).Where(u.Columns(o.Amount).Gt(o.WithContext(ctx).Select(o.Amount.Avg())).Find()
// SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders");
subQuery := u.WithContext(ctx).Select(u.Age.Avg()).Where(u.Name.Like("name%"))
users, err := u.WithContext(ctx).Select(u.Age.Avg().As("avgage")).Group(u.Name).Having(u.Columns(u.Age.Avg()).Gt(subQuery).Find()
// SELECT AVG(age) as avgage FROM `users` GROUP BY `name` HAVING AVG(age) > (SELECT AVG(age) FROM `users` WHERE name LIKE "name%")
From SubQuery
GORM позволяет использовать подзапрос в предложении FROM с методом Table, например:
u := query.Use(db).User
p := query.Use(db).Pet
users, err := gen.Table(u.WithContext(ctx).Select(u.Name, u.Age).As("u")).Where(u.Age.Eq(18)).Find()
// SELECT * FROM (SELECT `name`,`age` FROM `users`) as u WHERE `age` = 18
subQuery1 := u.WithContext(ctx).Select(u.Name)
subQuery2 := p.WithContext(ctx).Select(p.Name)
users, err := gen.Table(subQuery1.As("u"), subQuery2.As("p")).Find() **Первый фрагмент**
// Found user with `name` = `modi`, update it with Assign attributes
user, err := u.WithContext(ctx).Where(u.Name.Eq("modi")).Assign(u.Age.Value(20)).FirstOrInit()
// SELECT * FROM USERS WHERE name = modi' ORDER BY id LIMIT 1;
// user -> User{ID: 111, Name: "modi", Age: 20}
**Второй фрагмент**
###### FirstOrCreate
Get first matched record or create a new one with given conditions
```go
u := query.Use(db).User
// User not found, create a new record with give conditions
user, err := u.WithContext(ctx).Where(u.Name.Eq("non_existing")).FirstOrCreate()
// INSERT INTO "users" (name) VALUES ("non_existing");
// user -> User{ID: 112, Name: "non_existing"}
// Found user with `name` = `modi`
user, err := u.WithContext(ctx).Where(u.Name.Eq("modi")).FirstOrCreate()
// user -> User{ID: 111, Name: "modi", "Age": 18}
Create struct with more attributes if record not found, those Attrs
won’t be used to build SQL query
u := query.Use(db).User
// User not found, create it with give conditions and Attrs
user, err := u.WithContext(ctx).Where(u.Name.Eq("non_existing")).Attrs(u.Age.Value(20)).FirstOrCreate()
// SELECT * FROM users WHERE name = 'non_existing' ORDER BY id LIMIT 1;
// INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
// user -> User{ID: 112, Name: "non_existing", Age: 20}
// Found user with `name` = `modi`, attributes will be ignored
user, err := u.WithContext(ctx).Where(u.Name.Eq("modi")).Attrs(u.Age.Value(20)).FirstOrCreate()
// SELECT * FROM users WHERE name = 'modi' ORDER BY id LIMIT 1;
// user -> User{ID: 111, Name: "modi", Age: 18}
Assign
attributes to the record regardless it is found or not and save them back to the database.
u := query.Use(db).User
// User not found, initialize it with give conditions and Assign attributes
user, err := u.WithContext(ctx).Where(u.Name.Eq("non_existing")).Assign(u.Age.Value(20)).FirstOrCreate()
// SELECT * FROM users WHERE name = 'non_existing' ORDER BY id LIMIT 1;
// INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
// user -> User{ID: 112, Name: "non_existing", Age: 20}
// Found user with `name` = `modi`, update it with Assign attributes
user, err := u.WithContext(ctx).Where(u.Name.Eq("modi")).Assign(u.Age.Value(20)).FirstOrCreate(&user)
// SELECT * FROM users WHERE name = 'modi' ORDER BY id LIMIT 1;
// UPDATE users SET age=20 WHERE id = 111;
// user -> User{ID: 111, Name: "modi", Age: 20}
Третий фрагмент
GEN will auto-save associations as GORM do. The relationships (BelongsTo/HasOne/HasMany/Many2Many) reuse GORM's tag. This feature only support exist model for now.
There are 4 kind of relationship.
const (
HasOne RelationshipType = RelationshipType(schema.HasOne) // HasOneRel has one relationship
HasMany RelationshipType = RelationshipType(schema.HasMany) // HasManyRel has many relationships
BelongsTo RelationshipType = RelationshipType(schema.BelongsTo) // BelongsToRel belongs to relationship
Many2Many RelationshipType = RelationshipType(schema.Many2Many) // Many2ManyRel many to many relationship
)
package model
// exist model
type Customer struct {
gorm.Model
CreditCards []CreditCard `gorm:"foreignKey:CustomerRefer"`
}
type CreditCard struct {
gorm.Model
Number string
CustomerRefer uint
}
GEN will detect model's associations:
// specify model
g.ApplyBasic(model.Customer{}, model.CreditCard{})
// assoications will be detected and converted to code
package query
type customer struct {
...
CreditCards customerHasManyCreditCards
}
type creditCard struct{
...
}
The association have to be speified by gen.FieldRelate
card := g.GenerateModel("credit_cards")
customer := g.GenerateModel("customers", gen.FieldRelate(field.HasMany, "CreditCards", b,
&field.RelateConfig{
// RelateSlice: true,
GORMTag: "foreignKey:CustomerRefer",
}),
)
g.ApplyBasic(card, custormer)
GEN will generate models with associated field:
...
``` **Клиенты**
```type Customer struct {
ID int64 `gorm:"column:id;type:bigint(20) unsigned;primaryKey" json:"id"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime(3)" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime(3)" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:datetime(3)" json:"deleted_at"`
CreditCards []CreditCard `gorm:"foreignKey:CustomerRefer" json:"credit_cards"`
}
Кредитные карты
ID int64 `gorm:"column:id;type:bigint(20) unsigned;primaryKey" json:"id"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime(3)" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime(3)" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:datetime(3)" json:"deleted_at"`
CustomerRefer int64 `gorm:"column:customer_refer;type:bigint(20) unsigned" json:"customer_refer"`
}```
Если связанная модель уже существует, gen.FieldRelateModel может помочь вам построить ассоциации между ними.
```go
customer := g.GenerateModel("customers", gen.FieldRelateModel(field.HasMany, "CreditCards", model.CreditCard{},
&field.RelateConfig{
// RelateSlice: true,
GORMTag: "foreignKey:CustomerRefer",
}),
)
g.ApplyBasic(custormer)
type RelateConfig struct {
// specify field's type
RelatePointer bool // ex: CreditCard *CreditCard
RelateSlice bool // ex: CreditCards []CreditCard
RelateSlicePointer bool // ex: CreditCards []*CreditCard
JSONTag string // related field's JSON tag
GORMTag string // related field's GORM tag
NewTag string // related field's new tag
OverwriteTag string // related field's tag
}
user := model.User{
Name: "modi",
BillingAddress: Address{Address1: "Billing Address - Address 1"},
ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
Emails: []Email{
{Email: "modi@example.com"},
{Email: "modi-2@example.com"},
},
Languages: []Language{
{Name: "ZH"},
{Name: "EN"},
},
}
u := query.Use(db).User
u.WithContext(ctx).Select(u.Name).Create(&user)
// INSERT INTO "users" (name) VALUES ("jinzhu", 1, 2);
u.WithContext(ctx).Omit(u.BillingAddress.Field()).Create(&user)
// Пропустить создание BillingAddress при создании пользователя
u.WithContext(ctx).Omit(u.BillingAddress.Field("Address1")).Create(&user)
// Пропустить создание Address1 при создании BillingAddress для пользователя
u.WithContext(ctx).Omit(field.AssociationFields).Create(&user)
// Пропустить все ассоциации при создании пользователя```
Метод Field объединит серьёзное имя поля с «.», например: u.BillingAddress.Field («Address1», «Street») равно BillingAddress.Address1.Street.
###### Поиск ассоциаций
Найти подходящие ассоциации
```go
u := query.Use(db).User
languages, err = u.Languages.Model(&user).Find()
Найти ассоциации с условиями
q := query.Use(db)
u := q.User
languages, err = u.Languages.Where(q.Language.Name.In([]string{"ZH","EN"})).Model(&user).Find()
Добавить новые ассоциации для «многие ко многим», «имеет много», заменить текущую ассоциацию для «имеет один», «принадлежит»
u := query.Use(db).User
u.Languages.Model(&user).Append(&languageZH, &languageEN)
u.Languages.Model(&user).Append(&Language{Name: "DE"})
u.CreditCards.Model(&user).Append(&CreditCard{Number: "411111111111"})
Заменить текущие ассоциации новыми
u.Languages.Model(&user).Replace(&languageZH, &languageEN)
Удалить связь между источником и аргументами, если она существует, удалить только ссылку, не удалять эти объекты из БД.
u := query.Use(db).User
u.Languages.Model(&user).Delete(&languageZH, &languageEN)
u.Languages.Model(&user).Delete([]*Language{&languageZH, &languageEN}...)
``` **Обновления**
u.WithContext(ctx).Where(u.Activate.Is(true)).Update(u.Name, "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;
u.WithContext(ctx).Where(u.Activate.Is(true)).Update(u.Age, u.Age.Add(1))
// или
u.WithContext(ctx).Where(u.Activate.Is(true)).UpdateSimple(u.Age.Add(1))
// UPDATE users SET age=age+1, updated_at='2013-11-17 21:34:10' WHERE active=true;
u.WithContext(ctx).Where(u.Activate.Is(true)).UpdateSimple(u.Age.Zero())
// UPDATE users SET age=0, updated_at='2013-11-17 21:34:10' WHERE active=true;
**Обновляет несколько столбцов**
`Updates` поддерживает обновление с помощью `struct` или `map[string]interface{}`, при обновлении с помощью `struct` по умолчанию будут обновляться только ненулевые поля.
```go
u := query.Use(db).User
// Обновление атрибутов с помощью map
u.WithContext(ctx).Where(u.ID.Eq(111)).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
// Обновляет атрибуты с помощью struct
u.WithContext(ctx).Where(u.ID.Eq(111)).Updates(model.User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
// Обновить с выражением
u.WithContext(ctx).Where(u.ID.Eq(111)).UpdateSimple(u.Age.Add(1), u.Number.Add(1))
// UPDATE users SET age=age+1,number=number+1, updated_at='2013-11-17 21:34:10' WHERE id=111;
u.WithContext(ctx).Where(u.Activate.Is(true)).UpdateSimple(u.Age.Value(17), u.Number.Zero(), u.Birthday.Null())
// UPDATE users SET age=17, number=0, birthday=NULL, updated_at='2013-11-17 21:34:10' WHERE active=true;
Примечание: При обновлении с использованием
struct
, GEN будет обновлять только ненулевые поля, вы можете использоватьmap
для обновления атрибутов или использоватьSelect
, чтобы указать поля для обновления.
Обновление выбранных полей
Если вы хотите обновить выбранные поля или игнорировать некоторые поля при обновлении, вы можете использовать Select
и Omit
.
u := query.Use(db).User
// Select с Map
// ID пользователя равен 111:
u.WithContext(ctx).Select(u.Name).Where(u.ID.Eq(111)).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello' WHERE id=111;
u.WithContext(ctx).Omit(u.Name).Where(u.ID.Eq(111)).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
result, err := u.WithContext(ctx).Where(u.ID.Eq(111)).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
result.RowsAffected // количество затронутых строк
err // ошибка
Удалить запись
e := query.Use(db).Email
// Email ID равен 10
e.WithContext(ctx).Where(e.ID.Eq(10)).Delete()
// DELETE from emails where id = 10;
// Удалить с дополнительными условиями
e.WithContext(ctx).Where(e.ID.Eq(10), e.Name.Eq("modi")).Delete()
// DELETE from emails where id = 10 AND name = "modi";
result, err := e.WithContext(ctx).Where(e.ID.Eq(10), e.Name.Eq("modi")).Delete()
result.RowsAffected // затронутое количество строк
err // ошибка
Удалить с первичным ключом
GEN позволяет удалять объекты, используя первичный ключ (первичные ключи) с встроенным условием, это работает с числами.
u.WithContext(ctx).Where(u.ID.In(1,2,3)).Delete()
// DELETE FROM users WHERE id IN (1,2,3);
Пакетное удаление
Указанное значение не имеет первичного значения, GEN выполнит пакетное удаление, оно удалит все совпадающие записи.
e := query.Use(db).Email
e.WithContext(ctx).Where(e.Name.Like("%modi%")).Delete()
// DELETE from emails where email LIKE "%modi%";
Мягкое удаление
Если ваша модель включает поле gorm.DeletedAt
(которое включено в gorm.Model
), она автоматически получит возможность мягкого удаления!
При вызове Delete
запись НЕ будет удалена из базы данных, но GORM установит значение DeletedAt
на текущее время, и данные больше не будут найдены обычными методами запроса.
// Пакетное удаление
u.WithContext(ctx).Where(u.Age.Eq(20)).Delete() ```
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;
// Soft deleted records will be ignored when querying
users, err := u.WithContext(ctx).Where(u.Age.Eq(20)).Find()
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;
If you don’t want to include gorm.Model
, you can enable the soft delete feature like:
type User struct {
ID int
Deleted gorm.DeletedAt
Name string
}
You can find soft deleted records with Unscoped
users, err := db.WithContext(ctx).Unscoped().Where(u.Age.Eq(20)).Find()
// SELECT * FROM users WHERE age = 20;
You can delete matched records permanently with Unscoped
o.WithContext(ctx).Unscoped().Where(o.ID.Eq(10)).Delete()
// DELETE FROM orders WHERE id=10;
Method interface is an abstraction of query methods, all functions it contains are query methods and above comments describe the specific query conditions or logic.
SQL supports simple where
query or execute raw SQL. Simple query conditions wrapped by where()
, and raw SQL wrapped by sql()
(not required)
Method interface supports descriptive comment that describes how the method works. It starts with method name and followed descriptive message (not required). It is distinguished from query comment by blank line (with descriptive message) or space (without descriptive message).
type Method interface {
// where("name=@name and age=@age")
SimpleFindByNameAndAge(name string, age int) (gen.T, error)
// FindUserToMap query by id and return id->instance
//
// sql(select * from users where id=@id)
FindUserToMap(id int) (gen.M, error)
// InsertValue insert into users (name,age) values (@name,@age)
InsertValue(age int, name string) error
}
Return values must contain less than 1 gen.T
/gen.M
/gen.RowsAffected
and less than 1 error. You can also use bulitin type (like string
/ int
) as the return parameter, gen.T
represents return a single result struct's pointer, []gen.T
represents return an array of result structs' pointer,
gen.T
представляет указанную struct
или table
gen.M
представляет map[string]interface
gen.RowsAffected
представляет SQL выполненное rowsAffected
(тип: int64)@@table
представляет имя таблицы (если параметр метода не содержит переменную table
, GEN сгенерирует table
из модели структуры)@@<columnName>
представляет имя столбца или имя таблицы@<name>
представляет нормальную переменную запросаЛогические операции должны быть заключены в {{}}
, и в конце должен использоваться {{end}}
, все шаблоны поддерживают вложение
if
/ else if
/ else
условие принимает параметр bool или выражение операции, которое соответствует синтаксису Golang.where
Предложение where
будет вставлено только в том случае, если дочерние элементы что-то возвращают. Ключевое слово and
или or
перед предложением будет удалено. И and
будет добавлено автоматически, когда между условиями запроса нет ключевого слова соединения.Set
Предложение set
будет вставлено только в том случае, если дочерние элементы что-то возвращают. ,
перед массивом столбцов будет удален. И ,
будет добавлен автоматически, когда между столбцами запроса нет ключевого слова соединения.If
clause{{if cond1}}
// do something here
{{else if cond2}}
// do something here
{{else}}
// do something here
{{end}}
Пример использования в необработанном SQL:
// select * from users where {{if name !=""}} name=@name{{end}}
methond(name string) (gen.T,error)
Пример использования в шаблоне необработанного SQL:
select * from @@table where
{{if age>60}}
status="older"
{{else if age>30}}
status="middle-ager"
{{else if age>18}}
status="younger"
{{else}}
{{if sex=="male"}}
status="boys"
{{else}}
status="girls"
{{end}}
{{end}}
Where
clause{{where}}
// do something here
{{end}}
Пример использования в необработанном SQL
// select * from
``` **Где**
id=@id
**Метод (id int) ошибка**
```sql
select * from @@table
Пример использования в шаблоне SQL
{{where}}
{{if cond}}id=@id {{end}}
{{if name != ""}}@@key=@value{{end}}
{{end}}
Предложение set
{{set}}
// sepecify update expression here
{{end}}
Пример использования в SQL
// update users {{set}}name=@name{{end}} methond() error
Пример использования шаблона SQL
update @@table
{{set}}
{{if name!=""}} name=@name {{end}}
{{if age>0}} age=@age {{end}}
{{end}}
where id=@id
Интерфейс метода, пример
type Method interface {
// Where("name=@name and age=@age")
SimpleFindByNameAndAge(name string, age int) (gen.T, error)
// select * from users where id=@id
FindUserToMap(id int) (gen.M, error)
// sql(insert into @@table (name,age) values (@name,@age) )
InsertValue(age int, name string) error
// select name from @@table where id=@id
FindNameByID(id int) string
// select * from @@table
// {{where}}
// id>0
// {{if cond}}id=@id {{end}}
// {{if key!="" && value != ""}} or @@key=@value{{end}}
// {{end}}
FindByIDOrCustom(cond bool, id int, key, value string) ([]gen.T, error)
// update @@table
// {{set}}
// update_time=now()
// {{if name != ""}}
// name=@name
// {{end}}
// {{end}}
// {{where}}
// id=@id
// {{end}}
UpdateName(name string, id int) (gen.RowsAffected,error)
}
Умный выбор полей
GEN позволяет выбирать конкретные поля с помощью Select. Если вы часто используете это в своём приложении, возможно, вы захотите определить меньшую структуру для использования API, которая может автоматически выбирать конкретные поля, например:
type User struct {
ID uint
Name string
Age int
Gender string
// hundreds of fields
}
type APIUser struct {
ID uint
Name string
}
type Method interface{
// select * from user
FindSome() ([]APIUser, error)
}
apiusers, err := u.WithContext(ctx).Limit(10).FindSome()
// SELECT `id`, `name` FROM `users` LIMIT 10
Подсказки оптимизатора позволяют контролировать, какой план выполнения запроса выбирает оптимизатор запросов. GORM поддерживает его с помощью gorm.io/hints, например:
import "gorm.io/hints"
u := query.Use(db).User
users, err := u.WithContext(ctx).Clauses(hints.New("MAX_EXECUTION_TIME(10000)")).Find()
// SELECT * /*+ MAX_EXECUTION_TIME(10000) */ FROM `users`
Индексы позволяют передавать подсказки индекса в базу данных на случай, если планировщик запросов запутается.
import "gorm.io/hints"
u := query.Use(db).User
users, err := u.WithContext(ctx).Clauses(hints.UseIndex("idx_user_name")).Find()
// SELECT * FROM `users` USE INDEX (`idx_user_name`)
users, err := u.WithContext(ctx).Clauses(hints.ForceIndex("idx_user_name", "idx_user_id").ForJoin()).Find()
// SELECT * FROM `users` FORCE INDEX FOR JOIN (`idx_user_name`,`idx_user_id`)"
Установите GEN как двоичный инструмент:
go install gorm.io/gen/tools/gentool@latest
Использование:
$ gentool -h
Usage of gentool:
-db string
input mysql or postgres or sqlite or sqlserver. consult[https://gorm.io/docs/connecting_to_the_database.html] (default "mysql")
-dsn string
consult[https://gorm.io/docs/connecting_to_the_database.html]
-fieldNullable
generate with pointer when field is nullable
-fieldWithIndexTag
generate field with gorm index tag
-fieldWithTypeTag
generate field with gorm column type tag
-modelPkgName string
generated model code's package name
-outFile string
query code file name, default: gen.go
-outPath string
specify a directory for output (default "./dao/query")
-tables string
enter the required data table or leave it blank
-withUnitTest
generate unit test for query code
Пример:
gentool -dsn "user:pwd@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local" -tables "orders,doctor"
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )