一、数据库
import "github.com/jinzhu/gorm"
import _ "github.com/jinzhu/gorm/dialects/mysql"
// import _ "github.com/jinzhu/gorm/dialects/postgres"
// import _ "github.com/jinzhu/gorm/dialects/sqlite"
// import _ "github.com/jinzhu/gorm/dialects/mssql"
1.1. mysql
db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local") //parseTime不设置会导致查询参数时报错,loc不设置会导致时区变为0时区
defer db.Close()
1.2. PostgreSQL
db, err := gorm.Open("postgres", "host=myhost user=gorm dbname=gorm sslmode=disable password=mypassword")
defer db.Close()
1.3. Sqlite3
db, err := gorm.Open("sqlite3", "/tmp/gorm.db")
defer db.Close()
二、模型
基本模型定义gorm.Model
// 基本模型的定义
type Model struct {
ID uint `gorm:"primary_key"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
}
// 你可以将它嵌入到你自己的 Model 中,也可以完全使用自己的 Model。
type User struct {
gorm.Model
Name string
}
// 不使用gorm.Model定义模型
type User struct {
ID int
Name string
}
1.1 支持的结构体标记(Struct tags)
结构体标记(Tag) | 描述 |
---|---|
Column | 指定列名 |
Type | 指定列数据类型 |
Size | 指定列大小, 默认值255 |
PRIMARY_KEY | 将列指定为主键 |
UNIQUE | 将列指定为唯一 |
DEFAULT | 指定列默认值 |
PRECISION | 指定列精度 |
NOT NULL | 将列指定为非 NULL |
AUTO_INCREMENT | 指定列是否为自增类型 |
INDEX | 创建具有或不带名称的索引, 如果多个索引同名则创建复合索引 |
UNIQUE_INDEX | 和 INDEX 类似,只不过创建的是唯一索引 |
EMBEDDED | 将结构设置为嵌入 |
EMBEDDED_PREFIX | 设置嵌入结构的前缀 |
- | 忽略此字段 |
1.2 关联 struct 的标记(tags)
结构体标记(Tag) | 描述 |
---|---|
MANY2MANY | 指定连接表 |
FOREIGNKEY | 设置外键 |
ASSOCIATION_FOREIGNKEY | 设置关联外键 |
POLYMORPHIC | 指定多态类型 |
POLYMORPHIC_VALUE | 指定多态值 |
JOINTABLE_FOREIGNKEY | 指定连接表的外键 |
ASSOCIATION_JOINTABLE_FOREIGNKEY | 指定连接表的关联外键 |
SAVE_ASSOCIATIONS | 是否自动完成 save 的相关操作 |
ASSOCIATION_AUTOUPDATE | 是否自动完成 update 的相关操作 |
ASSOCIATION_AUTOCREATE | 是否自动完成 create 的相关操作 |
ASSOCIATION_SAVE_REFERENCE | 是否自动完成引用的 save 的相关操作 |
PRELOAD | 是否自动完成预加载的相关操作 |
实例
type User struct {
gorm.Model
Name string `gorm:"primary_key"` // 设置Name为主键
Age sql.NullInt64
Birthday *time.Time
Email string `gorm:"type:varchar(100);unique_index"`
Role string `gorm:"size:255"` // 设置字段大小为255
MemberNumber *string `gorm:"unique;not null"` // 设置会员号(member number)唯一并且不为空
Num int `gorm:"AUTO_INCREMENT"` // 设置 num 为自增类型
Address string `gorm:"index:addr"` // 给address字段创建名为addr的索引
IgnoreMe int `gorm:"-"` // 忽略本字段
}
2.1 默认表名
type User struct {} // 默认表名是`users`
// 设置User的表名为`profiles`
func (User) TableName() string {
return "profiles"
}
// 全局禁用表名复数
db.SingularTable(true) // 如果设置为true,`User`的默认表名为`user`,使用`TableName`设置的表名不受影响
//自定义表名设置函数
gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string) string {
return "prefix_" + defaultTableName;
}
// 使用User结构体创建名为`deleted_users`的表
db.Table("profiles").CreateTable(&User{})
2.2 列名
type User struct {
ID uint `gorm:"column:_id"` // 设置列名为 `_id`
Name string // 默认列名为 `name`
DeletedAt time.Time // 默认列名为 `deleted_at`
CreatedAt time.Time // 默认列名为 `created_at`
}
注意:
在创建数据时,将会自动设置CreatedAt为当前时间.
在删除数据时,将会自动设置DeletedAt为当前时间,而不是直接将记录从数据库中删除
3.1 外键
type Todo struct {
Id uint `gorm:"primary_key;unique;not null"`
Records []Record `gorm:"ForeignKey:TodoId"`
...
}
type Record struct {
TodoId uint
...
}
使用`gorm:"ForeignKey:TodoId"`将TodoID设置为外键,并于Todo表的Id(主键)关联。
`gorm:"ForeignKey:TodoId;AssociationForeignKey:Id"`AssociationForeignKey可以设置非主键字段关联外联
设置关联之后,使用 Create创建记录Todo时,可以自动创建todos和records表的记录
查询Todo表时,需要使用联表查询,否则Records字段为空
var todo internal.Todo
err := GetDBClient().Preload("Records").First(&todo, id).Error
if err != nil {
return internal.Todo{}, err
}
关联
// 根据Todo表中的主键查询record表中相关联的数据
GetDBClient().Model(&todo).Association("Records").Find(&records)
// 添加关联
GetDBClient().Model(&todo).Association("Records").Append(&records{Name: "DE"})
// 删除关联
GetDBClient().Model(&todo).Association("Records").Delete(&records{Name: "DE"})
// 替换关联
GetDBClient().Model(&todo).Association("Records").Replace(&records{Name: "DE"})
// 关联计数
GetDBClient().Model(&todo).Association("Records").Count()
// 删除关联
GetDBClient().Model(&todo).Association("Records").Clear()
三、操作
3.1. 自动迁移
自动迁移模式将保持更新到最新。 :::tips 修改User{}之后,自动迁移仅仅会创建表,创建缺少列和索引,并且不会改变现有列的类型或删除未使用的列以保护数据。 :::
db.AutoMigrate(&User{})
db.AutoMigrate(&User{}, &Product{}, &Order{})
// 创建表时添加表后缀
db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})
3.2. 检查表是否存在
// 检查模型`User`表是否存在
db.HasTable(&User{})
// 检查表`users`是否存在
db.HasTable("users")
3.3. 创建表
// 为模型`User`创建表
db.CreateTable(&User{})
// 创建表`users'时将“ENGINE = InnoDB”附加到SQL语句
db.Set("gorm:table_options", "ENGINE=InnoDB").CreateTable(&User{})
3.4. 删除表
// 删除模型`User`的表
db.DropTable(&User{})
// 删除表`users`
db.DropTable("users")
// 删除模型`User`的表和表`products`
db.DropTableIfExists(&User{}, "products")
3.5. 修改列
修改列的类型为给定值
// 修改模型`User`的description列的数据类型为`text`
db.Model(&User{}).ModifyColumn("description", "text")
3.6. 删除列
// 删除模型`User`的description列
db.Model(&User{}).DropColumn("description")
3.7. 添加外键
// 添加主键
// 1st param : 外键字段
// 2nd param : 外键表(字段)
// 3rd param : ONDELETE
// 4th param : ONUPDATE
db.Model(&User{}).AddForeignKey("city_id", "cities(id)", "RESTRICT", "RESTRICT")
3.8. 索引
// 为`name`列添加索引`idx_user_name`
db.Model(&User{}).AddIndex("idx_user_name", "name")
// 为`name`, `age`列添加索引`idx_user_name_age`
db.Model(&User{}).AddIndex("idx_user_name_age", "name", "age")
// 添加唯一索引
db.Model(&User{}).AddUniqueIndex("idx_user_name", "name")
// 为多列添加唯一索引
db.Model(&User{}).AddUniqueIndex("idx_user_name_age", "name", "age")
// 删除索引
db.Model(&User{}).RemoveIndex("idx_user_name")
四、CRUD
4.1 创建
4.1.1 创建记录
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
db.NewRecord(user) // 判断主键是否为空
db.Create(&user) // 插入数据,注意:这里必须使用&
4.1.2 默认值
type Animal struct {
ID int64
Name string `gorm:"default:'galeone'"`
Age int64
}
注意 所有字段的零值, 比如 0, ‘’, false 或者其它 零值,都不会保存到数据库内,但会使用他们的默认值。 如果你想避免这种情况,可以考虑使用指针或实现 Scanner/Valuer 接口,比如:
// 使用指针
type User struct {
gorm.Model
Name string
Age *int `gorm:"default:18"`
}
// 使用 Scanner/Valuer
type User struct {
gorm.Model
Name string
Age sql.NullInt64 `gorm:"default:18"` // sql.NullInt64 实现了Scanner/Valuer接口
}
4.2 查询
// 根据主键查询第一条记录
db.First(&user)
//// SELECT * FROM users ORDER BY id LIMIT 1;
// 查询指定的某条记录(仅当主键为整型时可用)
db.First(&user, 10)
//// SELECT * FROM users WHERE id = 10;
// 随机获取一条记录
db.Take(&user)
//// SELECT * FROM users LIMIT 1;
// 根据主键查询最后一条记录
db.Last(&user)
//// SELECT * FROM users ORDER BY id DESC LIMIT 1;
// 查询所有的记录
db.Find(&users)
//// SELECT * FROM users;
db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
//// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;
4.2.1 Where
// 获取所有匹配的记录
db.Where("name = ?", "jinzhu").Find(&users)
//// SELECT * FROM users WHERE name = 'jinzhu';
// IN
db.Where("name in (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// Struct
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;
// Map
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
// 主键切片
db.Where([]int64{20, 21, 22}).Find(&users)
//// SELECT * FROM users WHERE id IN (20, 21, 22);
提示 当通过结构体进行查询时,GORM将会只通过非零值字段查询,这意味着如果你的字段值为0,’’, false 或者其他 零值时,将不会被用于构建查询条件
例
var guest []Guest
err := db.Where("username=?", "123").Find(&guest).Error
if err != nil {
panic(err)
}
fmt.Println(guest)
4.2.2 Not条件查询
db.Not("name", "jinzhu").First(&user)
//// SELECT * FROM users WHERE name <> "jinzhu" LIMIT 1;
// Not In
db.Not("name", []string{"jinzhu", "jinzhu 2"}).Find(&users)
//// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");
4.2.3 Or条件查询
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
//// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';
4.2.4 select
db.Select("name, age").Find(&users)
//// SELECT name, age FROM users;
4.2.5 order
db.Order("age desc, name").Find(&users)
//// SELECT * FROM users ORDER BY age desc, name;
// Multiple orders
db.Order("age desc").Order("name").Find(&users)
//// SELECT * FROM users ORDER BY age desc, name;
4.2.6 Limit
db.Limit(3).Find(&users)
//// SELECT * FROM users LIMIT 3;
4.2.7 offset
db.Offset(3).Find(&users)
//// SELECT * FROM users OFFSET 3;
4.2.8 count
var count int
db.Find(&users).Count(&count)
4.2.9 join
rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
for rows.Next() {
...
}
db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)
// 多个连接与参数
db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)
FirstOrInit不存在就生成struct
获取第一个匹配的记录,或者使用给定的条件初始化一个新的记录(仅适用于struct,map条件)
// Unfound
db.FirstOrInit(&user, User{Name: "non_existing"})
//// user -> User{Name: "non_existing"}
// Found
db.Where(User{Name: "Jinzhu"}).FirstOrInit(&user)
//// user -> User{Id: 111, Name: "Jinzhu", Age: 20}
db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu"})
//// user -> User{Id: 111, Name: "Jinzhu", Age: 20}
Attrs
如果未找到记录,则使用参数初始化结构
// Unfound
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrInit(&user)
//// SELECT * FROM USERS WHERE name = 'non_existing';
//// user -> User{Name: "non_existing", Age: 20}
db.Where(User{Name: "non_existing"}).Attrs("age", 20).FirstOrInit(&user)
//// SELECT * FROM USERS WHERE name = 'non_existing';
//// user -> User{Name: "non_existing", Age: 20}
// Found
db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 30}).FirstOrInit(&user)
//// SELECT * FROM USERS WHERE name = jinzhu';
//// user -> User{Id: 111, Name: "Jinzhu", Age: 20}
Assign
将参数分配给结果,不管它是否被找到
// Unfound
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrInit(&user)
//// user -> User{Name: "non_existing", Age: 20}
// Found
db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 30}).FirstOrInit(&user)
//// SELECT * FROM USERS WHERE name = jinzhu';
//// user -> User{Id: 111, Name: "Jinzhu", Age: 30}
FirstOrCreate不存在就创建
获取第一个匹配的记录,或创建一个具有给定条件的新记录(仅适用于struct, map条件)
// Unfound
db.FirstOrCreate(&user, User{Name: "non_existing"})
//// INSERT INTO "users" (name) VALUES ("non_existing");
//// user -> User{Id: 112, Name: "non_existing"}
// Found
db.Where(User{Name: "Jinzhu"}).FirstOrCreate(&user)
//// user -> User{Id: 111, Name: "Jinzhu"}
Attrs
如果未找到记录,则为参数分配结构
// Unfound
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
//// SELECT * FROM users WHERE name = 'non_existing';
//// INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
//// user -> User{Id: 112, Name: "non_existing", Age: 20}
// Found
db.Where(User{Name: "jinzhu"}).Attrs(User{Age: 30}).FirstOrCreate(&user)
//// SELECT * FROM users WHERE name = 'jinzhu';
//// user -> User{Id: 111, Name: "jinzhu", Age: 20}
Assign
将其分配给记录,而不管它是否被找到,并保存回数据库。
// Unfound
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user)
//// SELECT * FROM users WHERE name = 'non_existing';
//// INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
//// user -> User{Id: 112, Name: "non_existing", Age: 20}
// Found
db.Where(User{Name: "jinzhu"}).Assign(User{Age: 30}).FirstOrCreate(&user)
//// SELECT * FROM users WHERE name = 'jinzhu';
//// UPDATE users SET age=30 WHERE id = 111;
//// user -> User{Id: 111, Name: "jinzhu", Age: 30}
4.3 更新
4.3.1 更新全部字段
db.First(&user)
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
//// UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;
4.3.2 更新部分字段
注意: user作为查询字段,查询时只会使用user的主键进行查询,若主键为空值,则修改所有字段。
更新单个字段Update
// 更新单个属性(如果更改)
db.Model(&user).Update("name", "hello")
//// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
// 使用组合条件更新单个属性
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;
更新多个字段Updates
// 使用`map`更新多个属性,只会更新这些更改的字段
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
//// UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
// 警告:当使用struct更新时,FORM将仅更新具有非空值的字段
// 对于下面的更新,什么都不会更新为"",0,false是其类型的空白值
db.Model(&user).Updates(User{Name: "", Age: 0, Actived: false})
4.3.3 批量更新
db.Table("users").Where("id IN (?)", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})
//// UPDATE users SET name='hello', age=18 WHERE id IN (10, 11);
// 使用struct更新仅适用于非零值,或使用map[string]interface{}
db.Model(User{}).Where("password = ?", 234).Updates(User{Name: "hello", Age: 18})
//// UPDATE users SET name='hello', age=18;
4.4 删除
警告 删除记录时,需要确保其主要字段具有值,GORM将使用主键删除记录,如果主要字段为空,GORM将删除模型的所有记录
如果模型有DeletedAt
字段,它将自动获得软删除功能! 那么在调用Delete
时不会从数据库中永久删除,而是只将字段DeletedAt
的值设置为当前时间。但查询数据时,不会查询到已经删除的数据。
4.4.1 单个删除
// 删除存在的记录
db.Delete(&email)
//// DELETE from emails where id=10;
4.4.2 批量删除
db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
//// DELETE from emails where email LIKE "%jinhu%";
db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
//// DELETE from emails where email LIKE "%jinhu%";
4.4.3 永久删除
// 使用Unscoped查找软删除的记录
db.Unscoped().Where("age = 20").Find(&users)
//// SELECT * FROM users WHERE age = 20;
// 使用Unscoped永久删除记录
db.Unscoped().Delete(&order)
//// DELETE FROM orders WHERE id=10;
五、回调
5.1. 创建对象
创建过程中可用的回调
// begin transaction 开始事物
BeforeSave
BeforeCreate
// save before associations 保存前关联
// update timestamp `CreatedAt`, `UpdatedAt` 更新`CreatedAt`, `UpdatedAt`时间戳
// save self 保存自己
// reload fields that have default value and its value is blank 重新加载具有默认值且其值为空的字段
// save after associations 保存后关联
AfterCreate
AfterSave
// commit or rollback transaction 提交或回滚事务
5.2. 更新对象
更新过程中可用的回调
// begin transaction 开始事物
BeforeSave
BeforeUpdate
// save before associations 保存前关联
// update timestamp `UpdatedAt` 更新`UpdatedAt`时间戳
// save self 保存自己
// save after associations 保存后关联
AfterUpdate
AfterSave
// commit or rollback transaction 提交或回滚事务
5.3. 删除对象
删除过程中可用的回调
// begin transaction 开始事物
BeforeDelete
// delete self 删除自己
AfterDelete
// commit or rollback transaction 提交或回滚事务
5.4. 查询对象
查询过程中可用的回调
// load data from database 从数据库加载数据
// Preloading (edger loading) 预加载(加载)
AfterFind
5.5. 回调示例
func (u *User) BeforeUpdate() (err error) {
if u.readonly() {
err = errors.New("read only user")
}
return
}
// 如果用户ID大于1000,则回滚插入
func (u *User) AfterCreate() (err error) {
if (u.Id > 1000) {
err = errors.New("user id is already greater than 1000")
}
return
}
gorm中的保存/删除操作正在事务中运行,因此在该事务中所做的更改不可见,除非提交。 如果要在回调中使用这些更改,则需要在同一事务中运行SQL。 所以你需要传递当前事务到回调,像这样:
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
tx.Model(u).Update("role", "admin")
return
}
func (u *User) AfterCreate(scope *gorm.Scope) (err error) {
scope.DB().Model(u).Update("role", "admin")
return
}
六、其余用法
1.1. 错误处理
执行任何操作后,如果发生任何错误,GORM将其设置为*DB
的Error
字段
if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil {
// 错误处理...
}
// 如果有多个错误发生,用`GetErrors`获取所有的错误,它返回`[]error`
db.First(&user).Limit(10).Find(&users).GetErrors()
// 检查是否返回RecordNotFound错误
db.Where("name = ?", "hello world").First(&user).RecordNotFound()
if db.Model(&user).Related(&credit_card).RecordNotFound() {
// 没有信用卡被发现处理...
}
1.2. 事物
要在事务中执行一组操作,一般流程如下。
// 开始事务
tx := db.Begin()
// 在事务中做一些数据库操作(从这一点使用'tx',而不是'db')
tx.Create(...)
// ...
// 发生错误时回滚事务
tx.Rollback()
// 或提交事务
tx.Commit()
1.2.1. 一个具体的例子
func CreateAnimals(db *gorm.DB) err {
tx := db.Begin()
// 注意,一旦你在一个事务中,使用tx作为数据库句柄
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
tx.Rollback()
return err
}
tx.Commit()
return nil
}
1.3. SQL构建
1.3.1. 执行原生SQL
db.Exec("DROP TABLE users;")
db.Exec("UPDATE orders SET shipped_at=? WHERE id IN (?)", time.Now, []int64{11,22,33})
// Scan
type Result struct {
Name string
Age int
}
var result Result
db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
1.3.2. sql.Row & sql.Rows
获取查询结果为*sql.Row
或*sql.Rows
row := db.Table("users").Where("name = ?", "jinzhu").Select("name, age").Row() // (*sql.Row)
row.Scan(&name, &age)
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {
...
rows.Scan(&name, &age, &email)
...
}
// Raw SQL
rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {
...
rows.Scan(&name, &age, &email)
...
}
1.3.3. 迭代中使用sql.Rows的Scan
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {
var user User
db.ScanRows(rows, &user)
// do something
}
1.4. 通用数据库接口sql.DB
从*gorm.DB
连接获取通用数据库接口*sql.DB
// 获取通用数据库对象`*sql.DB`以使用其函数
db.DB()
// Ping
db.DB().Ping()
1.4.1. 连接池
db.DB().SetMaxIdleConns(10)
db.DB().SetMaxOpenConns(100)
1.5. 复合主键
将多个字段设置为主键以启用复合主键
type Product struct {
ID string `gorm:"primary_key"`
LanguageCode string `gorm:"primary_key"`
}
1.6. 日志
Gorm有内置的日志记录器支持,默认情况下,它会打印发生的错误
// 启用Logger,显示详细日志
db.LogMode(true)
// 禁用日志记录器,不显示任何日志
db.LogMode(false)
// 调试单个操作,显示此操作的详细日志
db.Debug().Where("name = ?", "jinzhu").First(&User{})
1.6.1. 自定义日志
参考GORM的默认记录器如何自定义它https://github.com/jinzhu/gorm/blob/master/logger.go
db.SetLogger(gorm.Logger{revel.TRACE})
db.SetLogger(log.New(os.Stdout, "\r\n", 0))