GORM 极速入门

本文涉及的产品
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用版 2核4GB 50GB
简介: GORM 极速入门

一、基础概念


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 表。

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

比如会根据结构体的复数寻找表名,会使用 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 根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情
uniqueIndex index 相同,但创建的是唯一索引
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 会自动帮我们转换。

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;


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



相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
3月前
|
安全 程序员 API
GORM概述
GORM概述
41 0
|
3月前
|
SQL 数据库 索引
gorm普通的增删改查
gorm普通的增删改查
54 0
|
SQL 关系型数据库 MySQL
gin框架学习-Gorm入门指南
Snake Case命名风格,就是各个单词之间用下划线(_)分隔,首字母大写区分一个单词,例如: CreateTime的Snake Case风格命名为create_time
357 0
gin框架学习-Gorm入门指南
|
12月前
|
前端开发 Go API
Day08:GORM快速入门07 has many| 青训营
Day08:GORM快速入门07 has many| 青训营
|
3月前
|
SQL 数据库
|
3月前
|
3月前
|
SQL Go 数据库
gorm 教程 一(1)
gorm 教程 一
57 0
|
3月前
|
数据库
gorm 教程 一(2)
gorm 教程 一
62 0
|
3月前
|
SQL 数据库
gorm 教程 一(3)
gorm 教程 一
79 0
|
10月前
|
SQL 安全 数据库
GORM V2 写操作
GORM V2 写操作
38 0