一、数据库

  1. import "github.com/jinzhu/gorm"
  2. import _ "github.com/jinzhu/gorm/dialects/mysql"
  3. // import _ "github.com/jinzhu/gorm/dialects/postgres"
  4. // import _ "github.com/jinzhu/gorm/dialects/sqlite"
  5. // import _ "github.com/jinzhu/gorm/dialects/mssql"

1.1. mysql

  1. db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local") //parseTime不设置会导致查询参数时报错,loc不设置会导致时区变为0时区
  2. defer db.Close()

1.2. PostgreSQL

  1. db, err := gorm.Open("postgres", "host=myhost user=gorm dbname=gorm sslmode=disable password=mypassword")
  2. defer db.Close()

1.3. Sqlite3

  1. db, err := gorm.Open("sqlite3", "/tmp/gorm.db")
  2. defer db.Close()

二、模型

基本模型定义gorm.Model

  1. // 基本模型的定义
  2. type Model struct {
  3. ID uint `gorm:"primary_key"`
  4. CreatedAt time.Time
  5. UpdatedAt time.Time
  6. DeletedAt *time.Time
  7. }
  1. // 你可以将它嵌入到你自己的 Model 中,也可以完全使用自己的 Model。
  2. type User struct {
  3. gorm.Model
  4. Name string
  5. }
  6. // 不使用gorm.Model定义模型
  7. type User struct {
  8. ID int
  9. Name string
  10. }

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 是否自动完成预加载的相关操作

实例

  1. type User struct {
  2. gorm.Model
  3. Name string `gorm:"primary_key"` // 设置Name为主键
  4. Age sql.NullInt64
  5. Birthday *time.Time
  6. Email string `gorm:"type:varchar(100);unique_index"`
  7. Role string `gorm:"size:255"` // 设置字段大小为255
  8. MemberNumber *string `gorm:"unique;not null"` // 设置会员号(member number)唯一并且不为空
  9. Num int `gorm:"AUTO_INCREMENT"` // 设置 num 为自增类型
  10. Address string `gorm:"index:addr"` // 给address字段创建名为addr的索引
  11. IgnoreMe int `gorm:"-"` // 忽略本字段
  12. }

2.1 默认表名

  1. type User struct {} // 默认表名是`users`
  2. // 设置User的表名为`profiles`
  3. func (User) TableName() string {
  4. return "profiles"
  5. }
  6. // 全局禁用表名复数
  7. db.SingularTable(true) // 如果设置为true,`User`的默认表名为`user`,使用`TableName`设置的表名不受影响
  8. //自定义表名设置函数
  9. gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string) string {
  10. return "prefix_" + defaultTableName;
  11. }
  12. // 使用User结构体创建名为`deleted_users`的表
  13. db.Table("profiles").CreateTable(&User{})

2.2 列名

  1. type User struct {
  2. ID uint `gorm:"column:_id"` // 设置列名为 `_id`
  3. Name string // 默认列名为 `name`
  4. DeletedAt time.Time // 默认列名为 `deleted_at`
  5. CreatedAt time.Time // 默认列名为 `created_at`
  6. }

注意:
在创建数据时,将会自动设置CreatedAt为当前时间.
在删除数据时,将会自动设置DeletedAt为当前时间,而不是直接将记录从数据库中删除

3.1 外键

  1. type Todo struct {
  2. Id uint `gorm:"primary_key;unique;not null"`
  3. Records []Record `gorm:"ForeignKey:TodoId"`
  4. ...
  5. }
  6. type Record struct {
  7. TodoId uint
  8. ...
  9. }
  10. 使用`gorm:"ForeignKey:TodoId"`TodoID设置为外键,并于Todo表的Id(主键)关联。
  11. `gorm:"ForeignKey:TodoId;AssociationForeignKey:Id"`AssociationForeignKey可以设置非主键字段关联外联

设置关联之后,使用 Create创建记录Todo时,可以自动创建todos和records表的记录
查询Todo表时,需要使用联表查询,否则Records字段为空

  1. var todo internal.Todo
  2. err := GetDBClient().Preload("Records").First(&todo, id).Error
  3. if err != nil {
  4. return internal.Todo{}, err
  5. }

关联

  1. // 根据Todo表中的主键查询record表中相关联的数据
  2. GetDBClient().Model(&todo).Association("Records").Find(&records)
  3. // 添加关联
  4. GetDBClient().Model(&todo).Association("Records").Append(&records{Name: "DE"})
  5. // 删除关联
  6. GetDBClient().Model(&todo).Association("Records").Delete(&records{Name: "DE"})
  7. // 替换关联
  8. GetDBClient().Model(&todo).Association("Records").Replace(&records{Name: "DE"})
  9. // 关联计数
  10. GetDBClient().Model(&todo).Association("Records").Count()
  11. // 删除关联
  12. GetDBClient().Model(&todo).Association("Records").Clear()

三、操作

3.1. 自动迁移

自动迁移模式将保持更新到最新。 :::tips 修改User{}之后,自动迁移仅仅会创建表,创建缺少列和索引,并且不会改变现有列的类型或删除未使用的列以保护数据。 :::

  1. db.AutoMigrate(&User{})
  2. db.AutoMigrate(&User{}, &Product{}, &Order{})
  3. // 创建表时添加表后缀
  4. db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})

3.2. 检查表是否存在

  1. // 检查模型`User`表是否存在
  2. db.HasTable(&User{})
  3. // 检查表`users`是否存在
  4. db.HasTable("users")

3.3. 创建表

  1. // 为模型`User`创建表
  2. db.CreateTable(&User{})
  3. // 创建表`users'时将“ENGINE = InnoDB”附加到SQL语句
  4. db.Set("gorm:table_options", "ENGINE=InnoDB").CreateTable(&User{})

3.4. 删除表

  1. // 删除模型`User`的表
  2. db.DropTable(&User{})
  3. // 删除表`users`
  4. db.DropTable("users")
  5. // 删除模型`User`的表和表`products`
  6. db.DropTableIfExists(&User{}, "products")

3.5. 修改列

修改列的类型为给定值

  1. // 修改模型`User`的description列的数据类型为`text`
  2. db.Model(&User{}).ModifyColumn("description", "text")

3.6. 删除列

  1. // 删除模型`User`的description列
  2. db.Model(&User{}).DropColumn("description")

3.7. 添加外键

  1. // 添加主键
  2. // 1st param : 外键字段
  3. // 2nd param : 外键表(字段)
  4. // 3rd param : ONDELETE
  5. // 4th param : ONUPDATE
  6. db.Model(&User{}).AddForeignKey("city_id", "cities(id)", "RESTRICT", "RESTRICT")

3.8. 索引

  1. // 为`name`列添加索引`idx_user_name`
  2. db.Model(&User{}).AddIndex("idx_user_name", "name")
  3. // 为`name`, `age`列添加索引`idx_user_name_age`
  4. db.Model(&User{}).AddIndex("idx_user_name_age", "name", "age")
  5. // 添加唯一索引
  6. db.Model(&User{}).AddUniqueIndex("idx_user_name", "name")
  7. // 为多列添加唯一索引
  8. db.Model(&User{}).AddUniqueIndex("idx_user_name_age", "name", "age")
  9. // 删除索引
  10. db.Model(&User{}).RemoveIndex("idx_user_name")

四、CRUD

4.1 创建

4.1.1 创建记录

  1. user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
  2. db.NewRecord(user) // 判断主键是否为空
  3. db.Create(&user) // 插入数据,注意:这里必须使用&

4.1.2 默认值

  1. type Animal struct {
  2. ID int64
  3. Name string `gorm:"default:'galeone'"`
  4. Age int64
  5. }

注意 所有字段的零值, 比如 0, ‘’, false 或者其它 零值,都不会保存到数据库内,但会使用他们的默认值。 如果你想避免这种情况,可以考虑使用指针或实现 Scanner/Valuer 接口,比如:

  1. // 使用指针
  2. type User struct {
  3. gorm.Model
  4. Name string
  5. Age *int `gorm:"default:18"`
  6. }
  7. // 使用 Scanner/Valuer
  8. type User struct {
  9. gorm.Model
  10. Name string
  11. Age sql.NullInt64 `gorm:"default:18"` // sql.NullInt64 实现了Scanner/Valuer接口
  12. }

4.2 查询

  1. // 根据主键查询第一条记录
  2. db.First(&user)
  3. //// SELECT * FROM users ORDER BY id LIMIT 1;
  4. // 查询指定的某条记录(仅当主键为整型时可用)
  5. db.First(&user, 10)
  6. //// SELECT * FROM users WHERE id = 10;
  7. // 随机获取一条记录
  8. db.Take(&user)
  9. //// SELECT * FROM users LIMIT 1;
  10. // 根据主键查询最后一条记录
  11. db.Last(&user)
  12. //// SELECT * FROM users ORDER BY id DESC LIMIT 1;
  13. // 查询所有的记录
  14. db.Find(&users)
  15. //// SELECT * FROM users;
  16. db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
  17. //// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;

4.2.1 Where

  1. // 获取所有匹配的记录
  2. db.Where("name = ?", "jinzhu").Find(&users)
  3. //// SELECT * FROM users WHERE name = 'jinzhu';
  4. // IN
  5. db.Where("name in (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users)
  6. // LIKE
  7. db.Where("name LIKE ?", "%jin%").Find(&users)
  8. // AND
  9. db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
  10. // Time
  11. db.Where("updated_at > ?", lastWeek).Find(&users)
  12. db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
  13. // Struct
  14. db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
  15. //// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;
  16. // Map
  17. db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
  18. //// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
  19. // 主键切片
  20. db.Where([]int64{20, 21, 22}).Find(&users)
  21. //// SELECT * FROM users WHERE id IN (20, 21, 22);

提示 当通过结构体进行查询时,GORM将会只通过非零值字段查询,这意味着如果你的字段值为0,’’, false 或者其他 零值时,将不会被用于构建查询条件

  1. var guest []Guest
  2. err := db.Where("username=?", "123").Find(&guest).Error
  3. if err != nil {
  4. panic(err)
  5. }
  6. fmt.Println(guest)

4.2.2 Not条件查询

  1. db.Not("name", "jinzhu").First(&user)
  2. //// SELECT * FROM users WHERE name <> "jinzhu" LIMIT 1;
  3. // Not In
  4. db.Not("name", []string{"jinzhu", "jinzhu 2"}).Find(&users)
  5. //// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");

4.2.3 Or条件查询

  1. db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
  2. //// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';

4.2.4 select

  1. db.Select("name, age").Find(&users)
  2. //// SELECT name, age FROM users;

4.2.5 order

  1. db.Order("age desc, name").Find(&users)
  2. //// SELECT * FROM users ORDER BY age desc, name;
  3. // Multiple orders
  4. db.Order("age desc").Order("name").Find(&users)
  5. //// SELECT * FROM users ORDER BY age desc, name;

4.2.6 Limit

  1. db.Limit(3).Find(&users)
  2. //// SELECT * FROM users LIMIT 3;

4.2.7 offset

  1. db.Offset(3).Find(&users)
  2. //// SELECT * FROM users OFFSET 3;

4.2.8 count

  1. var count int
  2. db.Find(&users).Count(&count)

4.2.9 join

  1. rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
  2. for rows.Next() {
  3. ...
  4. }
  5. db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)
  6. // 多个连接与参数
  7. 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条件)

  1. // Unfound
  2. db.FirstOrInit(&user, User{Name: "non_existing"})
  3. //// user -> User{Name: "non_existing"}
  4. // Found
  5. db.Where(User{Name: "Jinzhu"}).FirstOrInit(&user)
  6. //// user -> User{Id: 111, Name: "Jinzhu", Age: 20}
  7. db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu"})
  8. //// user -> User{Id: 111, Name: "Jinzhu", Age: 20}

Attrs

如果未找到记录,则使用参数初始化结构

  1. // Unfound
  2. db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrInit(&user)
  3. //// SELECT * FROM USERS WHERE name = 'non_existing';
  4. //// user -> User{Name: "non_existing", Age: 20}
  5. db.Where(User{Name: "non_existing"}).Attrs("age", 20).FirstOrInit(&user)
  6. //// SELECT * FROM USERS WHERE name = 'non_existing';
  7. //// user -> User{Name: "non_existing", Age: 20}
  8. // Found
  9. db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 30}).FirstOrInit(&user)
  10. //// SELECT * FROM USERS WHERE name = jinzhu';
  11. //// user -> User{Id: 111, Name: "Jinzhu", Age: 20}

Assign

将参数分配给结果,不管它是否被找到

  1. // Unfound
  2. db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrInit(&user)
  3. //// user -> User{Name: "non_existing", Age: 20}
  4. // Found
  5. db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 30}).FirstOrInit(&user)
  6. //// SELECT * FROM USERS WHERE name = jinzhu';
  7. //// user -> User{Id: 111, Name: "Jinzhu", Age: 30}

FirstOrCreate不存在就创建

获取第一个匹配的记录,或创建一个具有给定条件的新记录(仅适用于struct, map条件)

  1. // Unfound
  2. db.FirstOrCreate(&user, User{Name: "non_existing"})
  3. //// INSERT INTO "users" (name) VALUES ("non_existing");
  4. //// user -> User{Id: 112, Name: "non_existing"}
  5. // Found
  6. db.Where(User{Name: "Jinzhu"}).FirstOrCreate(&user)
  7. //// user -> User{Id: 111, Name: "Jinzhu"}

Attrs

如果未找到记录,则为参数分配结构

  1. // Unfound
  2. db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
  3. //// SELECT * FROM users WHERE name = 'non_existing';
  4. //// INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
  5. //// user -> User{Id: 112, Name: "non_existing", Age: 20}
  6. // Found
  7. db.Where(User{Name: "jinzhu"}).Attrs(User{Age: 30}).FirstOrCreate(&user)
  8. //// SELECT * FROM users WHERE name = 'jinzhu';
  9. //// user -> User{Id: 111, Name: "jinzhu", Age: 20}

Assign

将其分配给记录,而不管它是否被找到,并保存回数据库。

  1. // Unfound
  2. db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user)
  3. //// SELECT * FROM users WHERE name = 'non_existing';
  4. //// INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
  5. //// user -> User{Id: 112, Name: "non_existing", Age: 20}
  6. // Found
  7. db.Where(User{Name: "jinzhu"}).Assign(User{Age: 30}).FirstOrCreate(&user)
  8. //// SELECT * FROM users WHERE name = 'jinzhu';
  9. //// UPDATE users SET age=30 WHERE id = 111;
  10. //// user -> User{Id: 111, Name: "jinzhu", Age: 30}

4.3 更新

4.3.1 更新全部字段

  1. db.First(&user)
  2. user.Name = "jinzhu 2"
  3. user.Age = 100
  4. db.Save(&user)
  5. //// 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

  1. // 更新单个属性(如果更改)
  2. db.Model(&user).Update("name", "hello")
  3. //// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
  4. // 使用组合条件更新单个属性
  5. db.Model(&user).Where("active = ?", true).Update("name", "hello")
  6. //// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;

更新多个字段Updates

  1. // 使用`map`更新多个属性,只会更新这些更改的字段
  2. db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
  3. //// UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
  4. // 警告:当使用struct更新时,FORM将仅更新具有非空值的字段
  5. // 对于下面的更新,什么都不会更新为"",0,false是其类型的空白值
  6. db.Model(&user).Updates(User{Name: "", Age: 0, Actived: false})

4.3.3 批量更新

  1. db.Table("users").Where("id IN (?)", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})
  2. //// UPDATE users SET name='hello', age=18 WHERE id IN (10, 11);
  3. // 使用struct更新仅适用于非零值,或使用map[string]interface{}
  4. db.Model(User{}).Where("password = ?", 234).Updates(User{Name: "hello", Age: 18})
  5. //// UPDATE users SET name='hello', age=18;

4.4 删除

警告 删除记录时,需要确保其主要字段具有值,GORM将使用主键删除记录,如果主要字段为空,GORM将删除模型的所有记录

如果模型有DeletedAt字段,它将自动获得软删除功能! 那么在调用Delete时不会从数据库中永久删除,而是只将字段DeletedAt的值设置为当前时间。但查询数据时,不会查询到已经删除的数据。

4.4.1 单个删除

  1. // 删除存在的记录
  2. db.Delete(&email)
  3. //// DELETE from emails where id=10;

4.4.2 批量删除

  1. db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
  2. //// DELETE from emails where email LIKE "%jinhu%";
  3. db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
  4. //// DELETE from emails where email LIKE "%jinhu%";

4.4.3 永久删除

  1. // 使用Unscoped查找软删除的记录
  2. db.Unscoped().Where("age = 20").Find(&users)
  3. //// SELECT * FROM users WHERE age = 20;
  4. // 使用Unscoped永久删除记录
  5. db.Unscoped().Delete(&order)
  6. //// DELETE FROM orders WHERE id=10;

五、回调

5.1. 创建对象

创建过程中可用的回调

  1. // begin transaction 开始事物
  2. BeforeSave
  3. BeforeCreate
  4. // save before associations 保存前关联
  5. // update timestamp `CreatedAt`, `UpdatedAt` 更新`CreatedAt`, `UpdatedAt`时间戳
  6. // save self 保存自己
  7. // reload fields that have default value and its value is blank 重新加载具有默认值且其值为空的字段
  8. // save after associations 保存后关联
  9. AfterCreate
  10. AfterSave
  11. // commit or rollback transaction 提交或回滚事务

5.2. 更新对象

更新过程中可用的回调

  1. // begin transaction 开始事物
  2. BeforeSave
  3. BeforeUpdate
  4. // save before associations 保存前关联
  5. // update timestamp `UpdatedAt` 更新`UpdatedAt`时间戳
  6. // save self 保存自己
  7. // save after associations 保存后关联
  8. AfterUpdate
  9. AfterSave
  10. // commit or rollback transaction 提交或回滚事务

5.3. 删除对象

删除过程中可用的回调

  1. // begin transaction 开始事物
  2. BeforeDelete
  3. // delete self 删除自己
  4. AfterDelete
  5. // commit or rollback transaction 提交或回滚事务

5.4. 查询对象

查询过程中可用的回调

  1. // load data from database 从数据库加载数据
  2. // Preloading (edger loading) 预加载(加载)
  3. AfterFind

5.5. 回调示例

  1. func (u *User) BeforeUpdate() (err error) {
  2. if u.readonly() {
  3. err = errors.New("read only user")
  4. }
  5. return
  6. }
  7. // 如果用户ID大于1000,则回滚插入
  8. func (u *User) AfterCreate() (err error) {
  9. if (u.Id > 1000) {
  10. err = errors.New("user id is already greater than 1000")
  11. }
  12. return
  13. }

gorm中的保存/删除操作正在事务中运行,因此在该事务中所做的更改不可见,除非提交。 如果要在回调中使用这些更改,则需要在同一事务中运行SQL。 所以你需要传递当前事务到回调,像这样:

  1. func (u *User) AfterCreate(tx *gorm.DB) (err error) {
  2. tx.Model(u).Update("role", "admin")
  3. return
  4. }
  1. func (u *User) AfterCreate(scope *gorm.Scope) (err error) {
  2. scope.DB().Model(u).Update("role", "admin")
  3. return
  4. }

六、其余用法

1.1. 错误处理

执行任何操作后,如果发生任何错误,GORM将其设置为*DBError字段

  1. if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil {
  2. // 错误处理...
  3. }
  4. // 如果有多个错误发生,用`GetErrors`获取所有的错误,它返回`[]error`
  5. db.First(&user).Limit(10).Find(&users).GetErrors()
  6. // 检查是否返回RecordNotFound错误
  7. db.Where("name = ?", "hello world").First(&user).RecordNotFound()
  8. if db.Model(&user).Related(&credit_card).RecordNotFound() {
  9. // 没有信用卡被发现处理...
  10. }

1.2. 事物

要在事务中执行一组操作,一般流程如下。

  1. // 开始事务
  2. tx := db.Begin()
  3. // 在事务中做一些数据库操作(从这一点使用'tx',而不是'db')
  4. tx.Create(...)
  5. // ...
  6. // 发生错误时回滚事务
  7. tx.Rollback()
  8. // 或提交事务
  9. tx.Commit()

1.2.1. 一个具体的例子

  1. func CreateAnimals(db *gorm.DB) err {
  2. tx := db.Begin()
  3. // 注意,一旦你在一个事务中,使用tx作为数据库句柄
  4. if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
  5. tx.Rollback()
  6. return err
  7. }
  8. if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
  9. tx.Rollback()
  10. return err
  11. }
  12. tx.Commit()
  13. return nil
  14. }

1.3. SQL构建

1.3.1. 执行原生SQL

  1. db.Exec("DROP TABLE users;")
  2. db.Exec("UPDATE orders SET shipped_at=? WHERE id IN (?)", time.Now, []int64{11,22,33})
  3. // Scan
  4. type Result struct {
  5. Name string
  6. Age int
  7. }
  8. var result Result
  9. db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)

1.3.2. sql.Row & sql.Rows

获取查询结果为*sql.Row*sql.Rows

  1. row := db.Table("users").Where("name = ?", "jinzhu").Select("name, age").Row() // (*sql.Row)
  2. row.Scan(&name, &age)
  3. rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
  4. defer rows.Close()
  5. for rows.Next() {
  6. ...
  7. rows.Scan(&name, &age, &email)
  8. ...
  9. }
  10. // Raw SQL
  11. rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows() // (*sql.Rows, error)
  12. defer rows.Close()
  13. for rows.Next() {
  14. ...
  15. rows.Scan(&name, &age, &email)
  16. ...
  17. }

1.3.3. 迭代中使用sql.Rows的Scan

  1. rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
  2. defer rows.Close()
  3. for rows.Next() {
  4. var user User
  5. db.ScanRows(rows, &user)
  6. // do something
  7. }

1.4. 通用数据库接口sql.DB

*gorm.DB连接获取通用数据库接口*sql.DB

  1. // 获取通用数据库对象`*sql.DB`以使用其函数
  2. db.DB()
  3. // Ping
  4. db.DB().Ping()

1.4.1. 连接池

  1. db.DB().SetMaxIdleConns(10)
  2. db.DB().SetMaxOpenConns(100)

1.5. 复合主键

将多个字段设置为主键以启用复合主键

  1. type Product struct {
  2. ID string `gorm:"primary_key"`
  3. LanguageCode string `gorm:"primary_key"`
  4. }

1.6. 日志

Gorm有内置的日志记录器支持,默认情况下,它会打印发生的错误

  1. // 启用Logger,显示详细日志
  2. db.LogMode(true)
  3. // 禁用日志记录器,不显示任何日志
  4. db.LogMode(false)
  5. // 调试单个操作,显示此操作的详细日志
  6. db.Debug().Where("name = ?", "jinzhu").First(&User{})

1.6.1. 自定义日志

参考GORM的默认记录器如何自定义它https://github.com/jinzhu/gorm/blob/master/logger.go

  1. db.SetLogger(gorm.Logger{revel.TRACE})
  2. db.SetLogger(log.New(os.Stdout, "\r\n", 0))

参考:

官方原版
GORM文档 github文档