likes
comments
collection
share

GORM 极速入门

作者站长头像
站长
· 阅读数 12

一、基础概念

ORM(Object Relational Mapping),意思是对象关系映射。

数据库会提供官方客户端驱动,但是需要自己处理 SQL 和结构体的转换。

使用 ORM 框架让我们避免转换,写出一些无聊的冗余代码。理论上 ORM 框架可以让我们脱离 SQL,但实际上还是需要懂 SQL 才可以使用 ORM。

我本人是非常排斥使用 ORM 框架的,原因有两点。

一、不自由,我不能随心所欲的控制我的数据库。

二、性能差,比官方客户端驱动直接编写 SQL 的效率低 3-5 倍。

不过 ORM 也有很多优点,它可以在一定程度上让新手避免慢 SQL。

也有一些文章讨论过 ORM 的利弊。比如这篇:orm_is_an_antipattern

总的来说,是否使用 ORM 框架取决于一个项目的开发人员组织结构。

老手渴望自由,新手需要规则。世界上新手多,老手就要做出一些迁就。

gorm 是一款用 Golang 开发的 orm 框架,目前已经成为在 Golang Web 开发中最流行的 orm 框架之一。本文将对 gorm 中常用的 API 进行讲解,帮助你快速学会 gorm。

除了 gorm,你还有其他选择,比如 sqlxsqlc

二、连接 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.")
}

三、声明模型

每一张表都会对应一个模型(结构体)。

比如数据库中有一张 goods 表。

GORM 极速入门

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 制定了很多约定,并按照约定大于配置的思想工作。

比如会根据结构体的复数寻找表名,会使用 ID 作为主键,会根据 CreateAt、UpdateAt 和 DeletedAt 表示创建时间、更新时间和删除时间。

gorm 提供了一个 Model 结构体,可以将它嵌入到自己的结构体中,省略以上几个字段。

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

嵌入到 goods 结构体中。

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

这样在每次创建不同的结构体时就可以省略创建 ID、CreatedAt、UpdatedAt、DeletedAt 这几个字段。

字段标签 tag

在创建模型时,可以给字段设置 tag 来对该字段一些属性进行定义。

比如创建 Post 结构体,我们希望 Title 映射为 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指定 db 列名
type列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INSTREMENT
size指定列大小,例如:size:256
primaryKey指定列为主键
unique指定列为唯一
default指定列的默认值
precision指定列的精度
scale指定列大小
not null指定列为 NOT NULL
autoIncrement指定列为自动增长
embedded嵌套字段
embeddedPrefix嵌入字段的列名前缀
autoCreateTime创建时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano
autoUpdateTime创建/更新时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli
index根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情
uniqueIndexindex 相同,但创建的是唯一索引
check创建检查约束,例如 check:age > 13,查看 约束 获取详情
<-设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限
->设置字段读的权限,->:false 无读权限
-忽略该字段,- 无读写权限

四、自动迁移

在数据库的表尚未初始化时,gorm 可以根据指定的结构体自动建表。

通过 db.AutoMigrate  方法根据 User 结构体,自动创建 user 表。如果表已存在,该方法不会有任何动作。

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

db.AutoMigrate(&User{})

建表的规则会把 user 调整为复数,并自动添加 gorm.Model 中的几个字段。由于很多数据库是不区分大小写的,如果采用 camelCase 风格命名法,在迁移数据库时会遇到很多问题,所以数据库的字段命名风格都是采用 underscorecase 风格命名法,gorm 会自动帮我们转换。

GORM 极速入门

等同于以下 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;

五、创建数据 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')

_需要注意:使用 select 时不会自动维护 created_at、updated_ad 和 deleted_at。

不插入指定字段

使用 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')

分批批量插入

在某些情况下,users 的数量可能非常大,此时可以使用 CreateInBatches 方法分批次批量插入。

假设有 6 条 user 数据,你想每次插入 2 条,这样就会执行 3 次 SQL。

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)

等同于依次执行以下 3 句 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 进行切割切片的,并没有使用 goroutine。

六、查询数据 Read Selete

查询单个对象

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 等函数中设置第二个参数,该参数被认作是 ID。可以选择 int 或 string 类型。

db.First(&user, 10)

db.First(&user, "10")

选择 string 类型的变量时,需要注意 SQL 注入问题。

查询多个对象(列表)

使用 Find 方法查询多个对象。

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

返回值会映射到 users 切片上。

依然可以通过访问返回值上的 Error 和 RowsAffected 字段获取异常和影响的行号。

result.Error
result.RowsAffected

设置查询条件 Where

gorm 提供了万能的 Where 方法,可以实现 =、<>、IN、LIKE、AND、>、<、BETWEEN 等方法,使用 ? 来占位。

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';

Where 快速设置条件的方法

传递 Struct、Map 和 切片时,可以实现更简便的设置条件。

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

结构体和 Map 的效果几乎是相等的。

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

两者唯一的不同之处在于 struct 中的零值字段不会查询。比如 0、""、false。

切片是查询主键。

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

等同于如下 SQL。

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

所有的查询,gorm 都会默认设置 tabel.deleted_at IS NULL  查询条件。

除了 Where 方法外,还有内联查询的方式,但是不推荐同时使用两种风格。

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

其他查询 Not & Or

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

选取特定字段 Select

使用 Select 方法。

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

其他操作

排序 Order

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

等同于以下 SQL。

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

分页 Limit Offset

Limit 和 Offset 可以单独使用,也可以组合使用。

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

分组 Group Having

根据 username 统计用户名的重复。

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;

去重 Distinct

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

等同于以下 SQL。

SELECT DISTINCT
	user_name
FROM
	users

连表 Join

在业务中不太建议使用 Join,而是使用多条查询来做多表关联。

七、更新数据 Update

更新所有字段

使用 Save 方法更新所有字段,即使是零值也会更新。

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 方法更新单列。

可以使用结构体作为选取条件,仅选择 ID。

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

也可以在 Model 中设置空结构体,使用 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 和 map 更新。当更新条件是 struct 时,零值不会更新,如果确保某列必定更新,使用 Select 选择该列。

更新选定字段 Selete Omit

使用 Select 和 Omit 方法。

批量更新

如果在 Model 中没有设置 ID,默认是批量更新。

八、删除数据 Delete

删除单条

使用 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。使用 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 语句。

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)

九、原生 SQL

除了上面的封装方法外,gorm 还提供了执行原生 SQL 的能力。

执行 SQL 并将结果映射到变量上

使用 Raw 方法配合 Scan 方法。

可以查询单条数据扫描并映射到结构体或 map 上。

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)

十、钩子 Hook

gorm 提供了 Hook 功能。可以在创建、查询、更新和删除之前和之后自动执行某些逻辑。

创建

gorm 提供了 4 个创建钩子,BeforeCreate、AfterCreate 和 BeforeSave、AfterSave。

假设现在需要添加一个 RecordID,并且在每次创建时生成一个 16 位的 uuid。

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

除此之外,还希望在存储之前打印生成的 uuid,在存储之后打印创建后的 id。

实现方式就是给模型结构体 User 添加 BeforeCreate 和 AfterCreate 两个方法。

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
}

更新

更新的 Hook 是 BeforeUpdate、AfterUpdate 和 BeforeSave、AfterSave,用法与创建一致。

查询

查询的 Hook 是 AfterFind,用法与创建一致。

删除

删除的 Hook 是 BeforeDelete 和 AfterDelete,用法与创建一致。

除了查询的 Hook 外,其他 Hook 都是在事务上运行的,一旦在函数中 return error 时,就会触发事务回滚。

十一、事务

事务保证了事务一致性,但会降低一些性能。gorm 的创建、修改和删除操作都在事务中执行。

如果不需要可以在初始化时禁用事务,可以提高 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
})

十二、日志

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,       // 禁用彩色打印
    },
)

日志的级别可以配置,可以设置 SilentErrorWarnInfo

全局模式开启

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

会话模式开启

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

自定义 Logger

gorm 提供了一个 Interface 接口,可以通过实现这个接口来自定义 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)
}