Быстрый старт

Go
Быстрый старт

1. Основные понятия

ORM (объектно-реляционное сопоставление), что означает объектно-реляционное сопоставление.

База данных предоставит официальный клиентский драйвер, но вам нужно самостоятельно выполнить преобразование SQL и структур.

Использование фреймворка ORM позволяет нам избежать переключения и написания скучного избыточного кода. Теоретически ORM-фреймворки могут вывести нас из SQL, но на практике нам все равно нужно знать SQL, чтобы использовать ORM.

Я сам очень не люблю использовать фреймворки ORM по двум причинам.

1. Не бесплатно, я не могу управлять своей базой данных так, как хочу.

Во-вторых, производительность низкая, в 3-5 раз ниже, чем эффективность написания SQL напрямую официальным драйвером клиента.

Тем не менее, ORM также имеет много преимуществ, он может в определенной степени избежать медленного SQL.

Есть также несколько статей, в которых обсуждаются плюсы и минусы ORM. Например это:orm_is_an_antipattern.

В целом, использование инфраструктуры ORM зависит от организации разработчиков проекта.

Ветераны жаждут свободы, новички хотят правил. В мире много новичков, и ветеранам приходится приспосабливаться.

gorm — это фреймворк форм, разработанный на Golang, который стал одним из самых популярных фреймворков в веб-разработке Golang. В этой статье объясняются часто используемые API-интерфейсы в gorm, которые помогут вам быстро освоить gorm.

Помимо горма, у вас есть другие варианты, такие какsqlxа такжеsqlc.

2. Подключиться к MySQL

gorm может подключаться к различным базам данных, просто нужны разные драйверы. Официальный в настоящее время поддерживает только четыре базы данных: MySQL, PostgreSQL, SQlite и SQL Server, но к другим базам данных можно получить доступ настраиваемым образом.

Ниже приведен пример подключения к mySQL.Во-первых, необходимо установить два пакета.

import (
    "gorm.io/driver/mysql" // gorm mysql 驱动包
	"gorm.io/gorm"// gorm
)

код подключения.

// MySQL 配置信息
username := "root"              // 账号
password := "xxxxxx" // 密码
host := "127.0.0.1"             // 地址
port := 3306                    // 端口
DBname := "gorm1"               // 数据库名称
timeout := "10s"                // 连接超时,10秒
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
// Open 连接
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
    panic("failed to connect mysql.")
}

В-третьих, модель декларации

Каждая таблица соответствует модели (структуре).

Например, в базе есть таблица товаров.

image.png

CREATE TABLE `gorm1`.`无标题`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
  `price` decimal(10, 2) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

Тогда ему будет соответствовать следующая структура.

type Goods struct {
    Id    int
    Name  string
    Price int
}

соглашение

gorm использует множество соглашений и работает с соглашением, а не с конфигурацией.

Например, имя таблицы будет найдено во множественном числе структуры, идентификатор будет использоваться в качестве первичного ключа, а время создания, время обновления и время удаления будут выражены в соответствии с CreateAt, UpdateAt и DeletedAt.

gorm предоставляет структуру модели, которую можно встроить в собственную структуру, опуская вышеуказанные поля.

type Model struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
}

Встраивается в структуру товаров.

type Goods struct {
    gorm.Model
    Id    int
    Name  string
    Price int
}

Таким образом, поля идентификатора создания, CreatedAt, UpdatedAt и DeletedAt можно опускать каждый раз, когда создается другая структура.

тег поля

При создании модели вы можете установить тег для поля, чтобы определить некоторые свойства поля.

Например, чтобы создать структуру Post, мы хотим, чтобы заголовок был сопоставлен с t, установил максимальную длину 256, и это поле было уникальным.

type Post struct {
	Title string `gorm:"column:t, size:256, unique:true"`
}

Эквивалентно следующему SQL.

CREATE TABLE `posts` (`t, size:256, unique:true` longtext)

Дополнительные функции см. в таблице ниже.

название ярлыка иллюстрировать
column Укажите имя столбца БД
type Тип данных столбца, рекомендуется использовать общий тип с хорошей совместимостью, например: все базы данных поддерживают bool, int, uint, float, string, time, bytes и могут использоваться с другими тегами, например:not null,size, autoIncrement… картинаvarbinary(8)Указание типа данных базы данных таким способом также поддерживается. При использовании указанного типа данных базы данных это должен быть полный тип данных базы данных, например:MEDIUMINT UNSIGNED not NULL AUTO_INSTREMENT
size Укажите размер столбца, например:size:256
primaryKey Укажите столбец в качестве первичного ключа
unique указанный столбец уникален
default Указывает значение по умолчанию для столбца
precision точность указанного столбца
scale указать размер столбца
not null Указывает, что столбец НЕ NULL
autoIncrement Указать автоинкремент столбца
embedded вложенные поля
embeddedPrefix Префикс имени столбца для встроенных полей
autoCreateTime Отслеживает текущее время при создании, дляintполе, которое отслеживает метку времени в секундах, вы можете использоватьnano/milliдля отслеживания временных меток в наносекундах и миллисекундах, например:autoCreateTime:nano
autoUpdateTime Отслеживать текущее время при создании/обновлении, дляintполе, которое отслеживает метку времени в секундах, вы можете использоватьnano/milliдля отслеживания временных меток в наносекундах и миллисекундах, например:autoUpdateTime:milli
index Создайте индекс на основе параметров, создайте составной индекс с одинаковым именем для нескольких полей, см.показательПолучить подробности
uniqueIndex а такжеindexТо же самое, но создает уникальный индекс
check Создайте проверочные ограничения, такие какcheck:age > 13,ПроверятьограничениеПолучить подробности
<- Установите разрешение на запись в поле,<-:createтолько создавать,<-:updateтолько обновление,<-:falseнет разрешения на запись,<-Создание и обновление разрешений
-> Установите разрешение на чтение поля,->:falseнет разрешения на чтение
- игнорировать это поле,-Нет прав на чтение и запись

4. Автоматическая миграция

Когда таблица базы данных не была инициализирована, горм может автоматически создать таблицу в соответствии с заданной структурой.

пройти черезdb.AutoMigrateМетод автоматически создает пользовательскую таблицу на основе структуры User. Если таблица уже существует, этот метод ничего не делает.

type User struct {
	gorm.Model
	UserName string
	Password string
}

db.AutoMigrate(&User{})

Правила создания таблиц настроят пользователя на множественное число и автоматически добавят несколько полей в горм.Модель. Поскольку многие базы данных не чувствительны к регистру, если вы используете номенклатуру стиля camelCase, вы столкнетесь со многими проблемами при переносе базы данных.Поэтому стиль именования полей базы данных использует номенклатуру стиля подчеркивания, и gorm автоматически поможет нам преобразовать ее.

image.png

Эквивалентно следующему SQL.

CREATE TABLE `gorm1`.`无标题`  (
  `id` bigint(0) UNSIGNED NOT NULL AUTO_INCREMENT,
  `created_at` datetime(3) NULL DEFAULT NULL,
  `updated_at` datetime(3) NULL DEFAULT NULL,
  `deleted_at` datetime(3) NULL DEFAULT NULL,
  `user_name` longtext CHARACTER SET utf8 COLLATE utf8_bin NULL,
  `password` longtext CHARACTER SET utf8 COLLATE utf8_bin NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_users_deleted_at`(`deleted_at`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

5. Создать данные Craete Insert

использоватьdb.CreateМетод, создается указатель на переданную структуру.

user := User{UserName: "l", Password: "ddd"}
result := db.Create(&user)

Эквивалентно следующему SQL.

INSERT INTO
    `users` ( `created_at`, `updated_at`, `deleted_at`, `user_name`, `password` )
VALUES
	(
		'2020-12-03 17:19:00.249',
		'2020-12-03 17:19:00.249',
		NULL,
	'l',
	'ddd')

gorm будет автоматически поддерживать поля created_at, updated_ad иDeleted_at.

Общие данные возвращаются после вставки

Ниже приведены некоторые часто используемые данные для вставки.

fmt.Println("ID:", user.ID)                       // 插入的主键
fmt.Println("error:", result.Error)               // 返回的 error
fmt.Println("rowsAffected:", result.RowsAffected) // 插入的条数

Вставить только указанные поля

Выберите указанное поле с помощью Select.

user := User{UserName: "lzq", Password: "ccc"}
result := db.Select("UserName").Create(&user)

Эквивалентно следующему SQL.

INSERT INTO `users` (`user_name`) VALUES ('lzq')

_Примечание: created_at, updated_ad иDeleted_at не поддерживаются автоматически при использовании select.

Не вставлять указанное поле

Используйте метод Omit для фильтрации некоторых полей.

result := db.Omit("UserName").Create(&user)

Объемная вставка

Когда требуется пакетная вставка, можно передать срез.

users := []User{
    {UserName: "lzq", Password: "aaa"},
    {UserName: "qqq", Password: "bbb"},
    {UserName: "gry", Password: "ccc"},
}
db.Create(&users)

Эквивалентно следующему SQL.

INSERT INTO `users` ( `created_at`, `updated_at`, `deleted_at`, `user_name`, `password` )
VALUES
	( '2020-12-03 18:08:47.478', '2020-12-03 18:08:47.478', NULL, 'lzq', 'aaa' ),(
		'2020-12-03 18:08:47.478',
		'2020-12-03 18:08:47.478',
		NULL,
		'qqq',
		'bbb'
		),(
		'2020-12-03 18:08:47.478',
		'2020-12-03 18:08:47.478',
		NULL,
	'gry',
	'ccc')

пакетная вставка

В некоторых случаях количество пользователей может быть очень большим, и в этом случае вы можете использоватьCreateInBatchesМетод пакетной вставки в пакетах.

Предположим, что есть 6 частей пользовательских данных, и вы хотите вставить 2 части за раз, что приведет к выполнению SQL 3 раза.

users := []User{
    {UserName: "lzq", Password: "aaa"},
    {UserName: "qqq", Password: "bbb"},
    {UserName: "gry", Password: "ccc"},
    {UserName: "lzq", Password: "aaa"},
    {UserName: "qqq", Password: "bbb"},
    {UserName: "gry", Password: "ccc"},
}

db.CreateInBatches(&users, 2)

Это эквивалентно последовательному выполнению следующих трех операторов SQL.

INSERT INTO `users` ( `created_at`, `updated_at`, `deleted_at`, `user_name`, `password` )
VALUES
	( '2020-12-03 18:15:20.602', '2020-12-03 18:15:20.602', NULL, 'lzq', 'aaa' ),(
		'2020-12-03 18:15:20.602',
		'2020-12-03 18:15:20.602',
		NULL,
	'qqq',
	'bbb')
INSERT INTO `users` ( `created_at`, `updated_at`, `deleted_at`, `user_name`, `password` )
VALUES
	( '2020-12-03 18:15:20.616', '2020-12-03 18:15:20.616', NULL, 'gry', 'ccc' ),(
		'2020-12-03 18:15:20.616',
		'2020-12-03 18:15:20.616',
		NULL,
	'lzq',
	'aaa')
INSERT INTO `users` ( `created_at`, `updated_at`, `deleted_at`, `user_name`, `password` )
VALUES
	( '2020-12-03 18:15:20.621', '2020-12-03 18:15:20.621', NULL, 'qqq', 'bbb' ),(
		'2020-12-03 18:15:20.621',
		'2020-12-03 18:15:20.621',
		NULL,
		'gry',
	'ccc'
	)

CreateInBatchesВнутри метода используется for для вырезания и нарезки и не используется горутина.

6. Запросить данные Чтение Выбрать

запросить один объект

gorm предоставляет методы First, Take, Last. они все прошлиLIMIT 1Для достижения, являются первичный ключ по возрастанию, несортированный и первичный ключ по убыванию.

user := User{}

// 获取第一条记录(主键升序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;

// 获取一条记录,没有指定排序字段
db.Take(&user)
// SELECT * FROM users LIMIT 1;

// 获取最后一条记录(主键降序)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;

Если ни один объект не запрошен, будет возвращена ошибка ErrRecordNotFound.

result := db.First(&user)
errors.Is(result.Error, gorm.ErrRecordNotFound)
result.RowsAffected

Запрос по первичному ключу

Установите второй параметр в таких функциях, как First/Take/Last, который считается идентификатором. Можно выбрать тип int или string.

db.First(&user, 10)

db.First(&user, "10")

При выборе переменных строкового типа необходимо помнить о проблемах с SQL-инъекциями.

Запрос нескольких объектов (списков)

Используйте метод Find для запроса нескольких объектов.

users := []User{}
result := db.Find(&users)

Возвращаемое значение сопоставляется со срезом пользователей.

Номер исключения и затронутой строки по-прежнему можно получить, обратившись к полям Error и RowsAffected в возвращаемом значении.

result.Error
result.RowsAffected

Установить условия запроса Где

gorm предоставляет универсальный метод Where, который может реализовывать =, , IN, LIKE, AND, >,

db.Where("name = ?", "l").First(&user)
// SELECT * FROM users WHERE user_name = 'l' ORDER BY id LIMIT 1;

// 获取全部匹配的记录
db.Where("name <> ?", "l").Find(&users)
// SELECT * FROM users WHERE user_name <> 'l';

// IN
db.Where("name IN ?", []string{"lzq", "qqq"}).Find(&users)
// SELECT * FROM users WHERE user_name IN ('lzq','qqq');

// LIKE
db.Where("name LIKE ?", "%l%").Find(&users)
// SELECT * FROM users WHERE user_name LIKE '%l%';

// AND
db.Where("name = ? AND age = ?", "lzq", "aaa").Find(&users)
// SELECT * FROM users WHERE user_name = 'lzq' AND password = aaa;

// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2020-11-01 00:00:00' AND '2020-11-08 00:00:00';

Где быстрый способ поставить условия

При передаче Structs, Maps и Slices вы можете добиться более простых условий.

db.Where(&User{UserName:"lzq", Password:"aaa"}).Find(&user)
db.Where(map[string]interface{}{"user_name": "lzq", "password": "aaa"}).Find(&user)

Эффект структуры и карты почти одинаков.

SELECT
	*
FROM
	`users`
WHERE
	`users`.`user_name` = 'lzq'
	AND `users`.`password` = 'aaa'
	AND `users`.`deleted_at` IS NULL

Единственная разница между ними заключается в том, что поля с нулевым значением в структуре не запрашиваются. Например, 0, "", ложь.

Срезы — это первичные ключи запроса.

db.Where([]int{10, 11}).Find(&user)

Эквивалентно следующему SQL.

SELECT
	*
FROM
	`users`
WHERE
	`users`.`id` IN ( 10, 11 )
	AND `users`.`deleted_at` IS NULL

Для всех запросов горм будет установлен по умолчаниюtabel.deleted_at IS NULLУсловия запроса.

В дополнение к методу Where существуют способы встроенных запросов, но использовать оба стиля одновременно не рекомендуется.

db.Find(&user, "user_name = ?", "lzq")
// SELECT * FROM users WHERE user_name = "lzq";

Другие запросы Не & Или

gorm также предоставляет методы Not и Or, но использовать их не рекомендуется, т.к. Where также может реализовать функции обоих, а запоминание дополнительных API, несомненно, увеличит умственную нагрузку.

db.Where("password = ?", "aaa").Not("user_name", "l").Or("id > ?", 10).Find(&users)

Эквивалентно следующему SQL.

SELECT * FROM `users` WHERE (( PASSWORD = 'aaa' )
	AND `user_name` <> 108
	OR id > 10
)
AND `users`.`deleted_at` IS NULL

Выберите конкретное поле Выберите

Используйте метод выбора.

db.Select("password").Where(&User{UserName:"lzq"}).Find(&user)

Эквивалентно следующему SQL.

SELECT
	`password`
FROM
	`users`
WHERE
	`users`.`user_name` = 'lzq'
	AND `users`.`deleted_at` IS NULL

другие операции

Порядок сортировки

db.Order("user_name desc, password").Find(&users)

Эквивалентно следующему SQL.

SELECT
	*
FROM
	`users`
WHERE
	`users`.`deleted_at` IS NULL
ORDER BY
	user_name DESC,
PASSWORD

Смещение лимита пейджинга

Ограничение и смещение можно использовать по отдельности или в комбинации.

db.Limit(3).Find(&users)
db.Offset(3).Find(&users)

db.Limit(2).Offset(3).Find(&users)

Эквивалентно следующему SQL.

SELECT
	*
FROM
	`users`
WHERE
	`users`.`deleted_at` IS NULL
	LIMIT 2 OFFSET 3

Группа Имеющая

Подсчет дубликатов имен пользователей на основе имени пользователя.

result := []map[string]interface{}{}
db.Model(&User{}).
  Select("user_name, SUM( id ) AS nums").
  Group("user_name").
  Find(&result)

Эквивалентно следующему SQL.

SELECT
	user_name,
	SUM( id ) AS nums
FROM
	users
GROUP BY
	user_name;

Дедупликация

result := []string{}
db.Model(&User{}).
  Distinct("user_name").
  Find(&result)

Эквивалентно следующему SQL.

SELECT DISTINCT
	user_name
FROM
	users

присоединиться к столу

Не рекомендуется использовать соединение в бизнесе, а использовать несколько запросов для связывания нескольких таблиц.

Семь, обновить данные

Обновить все поля

Используйте метод Сохранить, чтобы обновить все поля, даже нулевые значения.

db.First(&user)
user.UserName = ""
db.Save(&user)

Эквивалентно следующему SQL.

UPDATE `users`
SET `created_at` = '2020-12-03 15:12:08.548',
`updated_at` = '2020-12-04 09:17:40.891',
`deleted_at` = NULL,
`user_name` = '',
`password` = 'ddd'
WHERE
	`id` = 1

Обновить один столбец

Используйте методы Model и Update для обновления одного столбца.

Вы можете использовать структуру в качестве критерия выбора и выбирать только идентификаторы.

user.ID = 12
db.Model(&user).Update("user_name", "lzq")

Эквивалентно следующему SQL.

UPDATE `users`
SET `user_name` = 'lzq',
`updated_at` = '2020-12-04 09:16:45.263'
WHERE
	`id` = 12

Вы также можете задать пустую структуру в модели и использовать метод Where для самостоятельного выбора условий.

db.Model(&User{}).Where("user_name", "gry").Update("user_name", "gry2")

Эквивалентно следующему SQL.

UPDATE `users`
SET `user_name` = 'gry2',
`updated_at` = '2020-12-04 09:21:17.043'
WHERE
	`user_name` = 'gry'

Вы также можете комбинировать критерии выбора.

user.ID = 20
db.Model(&user).Where("username", "gry").Update("password", "123")

Эквивалентно следующему SQL.

UPDATE `users`
SET `password` = '123',
`updated_at` = '2020-12-04 09:25:30.872'
WHERE
	`username` = 'gry'
	AND `id` = 20

Обновить несколько столбцов

Используйте метод Updates для обновления нескольких столбцов. Поддержка обновлений структуры и карты. Когда условием обновления является struct, нулевое значение не будет обновлено.Если вы уверены, что столбец должен быть обновлен, используйте Select, чтобы выбрать столбец.

Обновить выбранные поля Удалить Пропустить

Используйте методы Select и Omit.

Массовое обновление

Если в модели не задан идентификатор, по умолчанию используется пакетное обновление.

Восемь, удалить данные Удалить

удалить один

Используйте метод Delete, чтобы удалить один фрагмент данных. Но указывать ID нужно, иначе будет удаляться пачками.

user.ID = 20
db.Delete(&user)

Эквивалентно следующему SQL.

UPDATE `users`
SET `deleted_at` = '2020-12-04 09:45:32.389'
WHERE
	`users`.`id` = 20
	AND `users`.`deleted_at` IS NULL

Установить условия удаления

Используйте метод Where для задания условий.

db.Where("user_name", "lzq").Delete(&user)

Эквивалентно следующему SQL.

UPDATE `users`
SET `deleted_at` = '2020-12-04 09:47:30.544'
WHERE
	`user_name` = 'lzq'
	AND `users`.`deleted_at` IS NULL

удалить по первичному ключу

Второй параметр может быть int, string. Остерегайтесь внедрения SQL при работе со строками.

db.Delete(&User{}, 20)

Эквивалентно следующему SQL.

UPDATE `users`
SET `deleted_at` = '2020-12-04 09:49:05.161'
WHERE
	`users`.`id` = 20
	AND `users`.`deleted_at` IS NULL

Также можно использовать слайсы []int, []string для пакетного удаления по ID.

db.Delete(&User{}, []string{"21", "22", "23"})

Эквивалентно следующему SQL.

UPDATE `users`
SET `deleted_at` = '2020-12-04 09:50:38.46'
WHERE
	`users`.`id` IN ( '21', '22', '23' )
	AND `users`.`deleted_at` IS NULL

пакетное удаление

Пустые структуры — это пакетные удаления.

Мягкое удаление (надгробие)

Если в структуре присутствует поле gorm.DeletedAt, автоматически получается возможность мягкого удаления.

Когда вызываются все методы Delete, они автоматически становятся операторами обновления.

UPDATE users SET deleted_at="2020-12-04 09:40" WHERE id = 31;

Обратимо удаленные данные автоматически игнорируются во время запроса.

SELECT * FROM users WHERE user_name = 'gry' AND deleted_at IS NULL;

Запрос мягких удаленных данных

Используйте метод Unscoped для поиска обратимо удаленных данных.

db.Unscoped().Where("user_name = gry").Find(&users)

Постоянное удаление (жесткое удаление, физическое удаление)

Безвозвратно удалить данные с помощью метода Unscoped.

user.ID = 14
db.Unscoped().Delete(&user)

9. Родной SQL

В дополнение к вышеупомянутым методам инкапсуляции, gorm также предоставляет возможность выполнять собственный SQL.

Выполнить SQL и сопоставить результаты с переменными

Используйте метод Raw с методом сканирования.

Отдельный фрагмент данных может быть запрошен для сканирования и сопоставления со структурой или картой.

db.
  Raw("SELECT id, record_id, user_name, password FROM users WHERE id = ?", 25).
  Scan(&user)

Также может быть сопоставлен с другими типами.

var userCount int
db.
  Raw("SELECT count(id) FROM users").
  Scan(&userCount)

Если возвращенный результат не соответствует типу входящей переменной сопоставления, значение переменной не изменится.

Выполнять только SQL без использования результатов

Используйте метод Exec для выполнения SQL.

db.Exec("UPDATE users SET password=? WHERE id = ?", "abcdefg", 22)

10. Крючок Крючок

gorm предоставляет функцию Hook. Определенная логика может выполняться автоматически до и после создания, запроса, обновления и удаления.

Создайте

gorm предоставляет 4 хука создания: BeforeCreate, AfterCreate и BeforeSave, AfterSave.

Теперь предположим, что вам нужно добавить RecordID и генерировать 16-битный uuid каждый раз, когда он создается.

type User struct {
	gorm.Model
	RecordID string
	UserName string
	Password string
}

В дополнение к этому я также хочу распечатать сгенерированный uuid перед сохранением и созданный идентификатор после сохранения.

Это можно сделать, добавив два метода, BeforeCreate и AfterCreate, в структуру модели User.

func (u *User) BeforeCreate(tx *gorm.DB) error {
	u.RecordID = uuid.New().String()
	fmt.Println("创建 User 开始,UUID 为:", u.RecordID)
	return nil
}

func (u *User) AfterCreate(tx *gorm.DB) error {
	fmt.Println("创建 User 完毕,ID 为:", u.ID)
	return nil
}

возобновить

Обновленные хуки — это BeforeUpdate, AfterUpdate и BeforeSave, AfterSave, и их использование такое же, как и при создании.

Запрос

Хук запроса — AfterFind, и его использование такое же, как и создание.

удалять

Удаленные хуки — это BeforeDelete и AfterDelete, и их использование такое же, как и при создании.

За исключением хука запроса, в транзакции запускаются другие хуки, и как только функция возвращает ошибку, запускается откат транзакции.

11. Дела

Транзакции гарантируют согласованность транзакций, но за счет некоторого снижения производительности. Создание, модификация и удаление gorms выполняются в транзакциях.

Если вам это не нужно, вы можете отключить транзакции при инициализации, что может повысить производительность примерно на 30%.

Глобально закрыть транзакцию

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
  SkipDefaultTransaction: true,
})

Закрыть транзакцию на уровне сеанса

tx := db.Session(&Session{SkipDefaultTransaction: true})
// 继续执行 SQL 时使用 tx 对象
tx.First(&user)

Выполнение SQL внутри транзакции

Предположим, теперь вам нужно добавить таблицу company для хранения информации о компании и создать таблицу company_users для связывания информации о пользователе и компании.

// 创建结构体
type Company struct {
	gorm.Model
	RecordID string
	Name     string
}

type CompanyUser struct {
	gorm.Model
	RecordID  string
	UserID    string
	CompanyID string
}

// 自动迁移
db.AutoMigrate(&Company{})
db.AutoMigrate(&CompanyUser{})

// 创建一家公司
company := Company{Name: "gxt"}
company.RecordID = uuid.New().String()
db.Save(&company)

// 在事务中执行
db.Transaction(func(tx *gorm.DB) error {
    // 创建用户
    u := User{UserName: "ztg", Password: "333"}
    result := tx.Create(&u)
    if err := result.Error; err != nil {
        return err
    }
    // 查询公司信息
    company2 := Company{}
    tx.First(&company2, company.ID)
    // 关联用户和公司
    result = tx.Create(&CompanyUser{UserID: u.RecordID, CompanyID: company2.RecordID})
    if err := result.Error; err != nil {
        return err
    }
    return nil
})

12. Журнал

gorm по умолчанию реализует Logger, который выводит только медленный SQL.

newLogger := logger.New(
    log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
    logger.Config{
        SlowThreshold: time.Second, // 慢 SQL 阈值
        LogLevel:      logger.Info, // Log level
        Colorful:      false,       // 禁用彩色打印
    },
)

Уровень лога можно настроить и установитьSilent,Error,Warn,Info.

глобальный режим включен

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
  Logger: newLogger,
})

Режим разговора включен

tx := db.Session(&Session{Logger: newLogger})

Пользовательский регистратор

gorm предоставляет интерфейс интерфейса, вы можете настроить Logger, реализовав этот интерфейс.

type Interface interface {
	LogMode(LogLevel) Interface
	Info(context.Context, string, ...interface{})
	Warn(context.Context, string, ...interface{})
	Error(context.Context, string, ...interface{})
	Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error)
}