|
|
@@ -101,4 +101,318 @@ type Model struct {
|
|
|
- `-:all` no read/write/migrate permission
|
|
|
- `comment` add comment for field when migration
|
|
|
|
|
|
+## Создание записи
|
|
|
|
|
|
+```golang
|
|
|
+user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
|
|
|
+
|
|
|
+result := db.Create(&user) // ОБЯЗАТЕЛЬНО передавать ссылку на Create
|
|
|
+
|
|
|
+users := []*User{
|
|
|
+ User{Name: "Jinzhu", Age: 18, Birthday: time.Now()},
|
|
|
+ User{Name: "Jackson", Age: 19, Birthday: time.Now()},
|
|
|
+}
|
|
|
+
|
|
|
+result := db.Create(users) // Передача нескольких записей сразу
|
|
|
+
|
|
|
+// Создать только с указанными полями
|
|
|
+db.Select("Name", "Age", "CreatedAt").Create(&user)
|
|
|
+
|
|
|
+// Инорировать при создании указанные поля
|
|
|
+db.Omit("Name", "Age", "CreatedAt").Create(&user)
|
|
|
+```
|
|
|
+
|
|
|
+Более сложный пример при вставке с решением конфликтов (и др.) есть в документации.
|
|
|
+
|
|
|
+## Получение записи
|
|
|
+
|
|
|
+```golang
|
|
|
+
|
|
|
+// Получить первую запись
|
|
|
+db.First(&user)
|
|
|
+
|
|
|
+// Получить последнюю запись
|
|
|
+db.Last(&user)
|
|
|
+
|
|
|
+// Получить по индексу
|
|
|
+db.First(&user, 10)
|
|
|
+
|
|
|
+// Получить несколько по указанным индексам в список
|
|
|
+var users []User
|
|
|
+db.Find(&users, []int{1,2,3})
|
|
|
+
|
|
|
+// Сделать выборку по условию
|
|
|
+db.Where("name <> ?", "jinzhu").Find(&users)
|
|
|
+db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
|
|
|
+db.Where("name LIKE ?", "%jin%").Find(&users)
|
|
|
+db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
|
|
|
+// С доаолнительным условием
|
|
|
+db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
|
|
|
+
|
|
|
+// По указанным полям
|
|
|
+db.Select("name", "age").Find(&users)
|
|
|
+
|
|
|
+// Отсортировать по полям
|
|
|
+db.Order("age desc, name").Find(&users)
|
|
|
+
|
|
|
+// Ограничить тремя записями
|
|
|
+db.Limit(3).Find(&users)
|
|
|
+
|
|
|
+// Ограничить 10 записей со смещением 5
|
|
|
+db.Limit(10).Offset(5).Find(&users)
|
|
|
+```
|
|
|
+
|
|
|
+Также можно объединять данные из разных таблиц
|
|
|
+
|
|
|
+```golang
|
|
|
+type result struct {
|
|
|
+ Name string
|
|
|
+ Email string
|
|
|
+}
|
|
|
+
|
|
|
+// Из таблицы users выбрать имя, из таблицы emails выбрать email, где user.id совпал
|
|
|
+db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
|
|
|
+// SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id
|
|
|
+
|
|
|
+// Решение объединения данных с предзагрузкой данных
|
|
|
+db.Joins("Company").Find(&users)
|
|
|
+// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id`;
|
|
|
+
|
|
|
+// Внутреннее объединение
|
|
|
+db.InnerJoins("Company").Find(&users)
|
|
|
+// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name`
|
|
|
+```
|
|
|
+
|
|
|
+Есть гораздо более сложные примеры, можно найти в документации
|
|
|
+
|
|
|
+## Цикл по записям
|
|
|
+
|
|
|
+```golang
|
|
|
+
|
|
|
+rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Rows()
|
|
|
+defer rows.Close()
|
|
|
+
|
|
|
+for rows.Next() {
|
|
|
+ var user User
|
|
|
+ // ScanRows метод в `gorm.DB`, может быть использован для сканирования записей
|
|
|
+ db.ScanRows(rows, &user)
|
|
|
+
|
|
|
+ // Что-то делаем
|
|
|
+}
|
|
|
+
|
|
|
+// работа почками записей по 100 штук
|
|
|
+result := db.Where("processed = ?", false).FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error {
|
|
|
+ for _, result := range results {
|
|
|
+ // пакетная обработка найденных записей
|
|
|
+ }
|
|
|
+
|
|
|
+ tx.Save(&results)
|
|
|
+
|
|
|
+ // tx.RowsAffected // число записей в этой пачке
|
|
|
+
|
|
|
+ // batch // Пачка 1, 2, 3
|
|
|
+
|
|
|
+ // returns error will stop future batches
|
|
|
+ return nil
|
|
|
+})
|
|
|
+```
|
|
|
+
|
|
|
+## Перехватчики/хуки
|
|
|
+
|
|
|
+```golang
|
|
|
+func (u *User) AfterFind(tx *gorm.DB) (err error) {
|
|
|
+ if u.Role == "" {
|
|
|
+ u.Role = "user"
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## Обновление записей
|
|
|
+
|
|
|
+```golang
|
|
|
+
|
|
|
+db.First(&user)
|
|
|
+
|
|
|
+user.Name = "jinzhu 2"
|
|
|
+user.Age = 100
|
|
|
+db.Save(&user)
|
|
|
+
|
|
|
+// Принудительное обновление поля
|
|
|
+db.Save(&User{Name: "jinzhu", Age: 100})
|
|
|
+
|
|
|
+// Обновление по условию
|
|
|
+db.Model(&user).Where("active = ?", true).Update("name", "hello")
|
|
|
+// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;
|
|
|
+
|
|
|
+
|
|
|
+// Обновление нескольких полей
|
|
|
+db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
|
|
|
+
|
|
|
+// Обновление только указанных полей (в SELECT)
|
|
|
+db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
|
|
|
+
|
|
|
+// Обновление с подзапросом (обновить имя копании у пользователя)
|
|
|
+db.Table("users as u").Where("name = ?", user.Name).Update("company_name", db.Table("companies as c").Select("name").Where("c.id = u.company_id"))
|
|
|
+```
|
|
|
+
|
|
|
+Перехватчик перед обновлением:
|
|
|
+
|
|
|
+```golang
|
|
|
+func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {
|
|
|
+ if u.Role == "admin" {
|
|
|
+ return errors.New("admin user not allowed to update")
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## Удаление записи
|
|
|
+
|
|
|
+```golang
|
|
|
+// Простое удаление. где ID=`10` (уже в структуре)
|
|
|
+db.Delete(&email)
|
|
|
+
|
|
|
+// Удаление с условием
|
|
|
+db.Where("name = ?", user.Name).Delete(&email)
|
|
|
+
|
|
|
+// Удаление с явным указанием ID
|
|
|
+db.Delete(&User{}, 10)
|
|
|
+
|
|
|
+// Удаление пачкой
|
|
|
+var users = []User{{ID: 1}, {ID: 2}, {ID: 3}}
|
|
|
+db.Delete(&user
|
|
|
+
|
|
|
+// Удаление пачкой
|
|
|
+db.Where("age = ?", 20).Delete(&User{})
|
|
|
+// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;
|
|
|
+
|
|
|
+// Физическое удаление
|
|
|
+db.Unscoped().Delete(&order)
|
|
|
+// DELETE FROM orders WHERE id=10
|
|
|
+```
|
|
|
+
|
|
|
+Перехватчик перед удалением:
|
|
|
+
|
|
|
+```golang
|
|
|
+func (u *User) BeforeDelete(tx *gorm.DB) (err error) {
|
|
|
+ if u.Role == "admin" {
|
|
|
+ return errors.New("admin user not allowed to delete")
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## Встроенные функции
|
|
|
+
|
|
|
+```golang
|
|
|
+db.Table("deleted_users").Count(&count)
|
|
|
+// SELECT count(1) FROM deleted_users;
|
|
|
+```
|
|
|
+
|
|
|
+## Сырые запросы
|
|
|
+
|
|
|
+```golang
|
|
|
+type Result struct {
|
|
|
+ ID int
|
|
|
+ Name string
|
|
|
+ Age int
|
|
|
+}
|
|
|
+
|
|
|
+// Выборка по условию
|
|
|
+var result Result
|
|
|
+db.Raw("SELECT id, name, age FROM users WHERE id = ?", 3).Scan(&result)
|
|
|
+
|
|
|
+// Выполнить что-то на севере по условию
|
|
|
+db.Exec("UPDATE orders SET shipped_at = ? WHERE id IN ?", time.Now(), []int64{1, 2, 3})
|
|
|
+
|
|
|
+// Посмотреть какой запрос сгенерирован
|
|
|
+sql := db.ToSQL(func(tx *gorm.DB) *gorm.DB {
|
|
|
+ return tx.Model(&User{}).Where("id = ?", 100).Limit(10).Order("age desc").Find(&[]User{})
|
|
|
+})
|
|
|
+// SELECT * FROM "users" WHERE id = 100 AND "users"."deleted_at" IS NULL ORDER BY age desc LIMIT 10
|
|
|
+```
|
|
|
+
|
|
|
+## Предзагрузка
|
|
|
+
|
|
|
+```golang
|
|
|
+type User struct {
|
|
|
+ gorm.Model
|
|
|
+ Username string
|
|
|
+ Orders []Order
|
|
|
+}
|
|
|
+
|
|
|
+type Order struct {
|
|
|
+ gorm.Model
|
|
|
+ UserID uint
|
|
|
+ Price float64
|
|
|
+}
|
|
|
+
|
|
|
+// Прямая предзагрузка
|
|
|
+db.Preload("Orders").Find(&users)
|
|
|
+
|
|
|
+// Предзагрузка с помощью левого соединения
|
|
|
+db.Joins("Company").Joins("Manager").Joins("Account").Find(&users, "users.id IN ?", []int{1,2,3,4,5})
|
|
|
+
|
|
|
+// Предзагрузка с условиями
|
|
|
+db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
|
|
|
+```
|
|
|
+
|
|
|
+## Использование контекста
|
|
|
+
|
|
|
+```golang
|
|
|
+ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
|
+defer cancel()
|
|
|
+
|
|
|
+db.WithContext(ctx).Find(&users)
|
|
|
+```
|
|
|
+
|
|
|
+## Автомиграция
|
|
|
+
|
|
|
+У автоиграции масса методов, все можно найти в документации.
|
|
|
+
|
|
|
+Ниже только часть из них:
|
|
|
+
|
|
|
+```golang
|
|
|
+// Создать таблицу, если не существует (самое простое)
|
|
|
+db.AutoMigrate(&User{})
|
|
|
+
|
|
|
+// Автомиграция по нескольким таблицам сразу (самое простое)
|
|
|
+db.AutoMigrate(&User{}, &Product{}, &Order{})
|
|
|
+
|
|
|
+// Создать таблицу для структуры `User`
|
|
|
+db.Migrator().CreateTable(&User{})
|
|
|
+
|
|
|
+// Переименовать таблицу
|
|
|
+db.Migrator().RenameTable(&User{}, &UserInfo{})
|
|
|
+
|
|
|
+// Добавить колонку
|
|
|
+db.Migrator().AddColumn(&User{}, "Name")
|
|
|
+
|
|
|
+// Удалить колонку
|
|
|
+db.Migrator().DropColumn(&User{}, "Name")
|
|
|
+```
|
|
|
+
|
|
|
+## Отображения (views)
|
|
|
+
|
|
|
+```golang
|
|
|
+// Запрос на создание отображения
|
|
|
+query := db.Model(&User{}).Where("age > ?", 20)
|
|
|
+db.Migrator().CreateView("users_pets", gorm.ViewOption{Query: query})
|
|
|
+db.Migrator().DropView("users_pets")
|
|
|
+```
|
|
|
+
|
|
|
+## Пользовательские типы
|
|
|
+
|
|
|
+```golang
|
|
|
+type User strict{
|
|
|
+ gorm.Model
|
|
|
+ Name string
|
|
|
+}
|
|
|
+
|
|
|
+// SetName -- устанавливает имя пользователя
|
|
|
+func (sf *User)SetName(name string){
|
|
|
+ sf.Name = name
|
|
|
+ db.Model(sf).Update("name", sf.Name)
|
|
|
+}
|
|
|
+```
|