目录

文章目录

  • 目录
  • 实现一个关系型数据库应用程序需要做什么?
  • GORM
  • 连接数据库
  • 表定义
    • Module Struct tags
  • 表操作
    • db.HasTable 表是否存在
    • db.CreateTable 创建表
    • db.DropTable 删除表
    • db.ModifyColumn 修改列
    • db.DropColumn 删除列
    • db.AddIndex 添加索引
    • db.RemoveIndex 删除索引
    • db.AddForeignKey 添加外键
    • db.RemoveForeignKey 删除外键
  • CRUD 操作
    • db.Create 插入
    • db.First、db.Find 查询
      • db.Where 选择
      • db.Select 投影
      • db.Joins 连接
      • db.Order 排序
      • db.Limit 限量
      • db.Offset 偏移量
      • db.Count 总数
    • db.Update 更新
      • 更新所有字段
      • 更新修改字段
      • 更新选定字段
    • db.Delete 删除
      • 软删除
  • 关系(Relationship)类型
    • Belongs To
    • Has One
    • Has Many
    • Many To Many
    • 关联(Associations)
      • 查找关联
      • 添加关联
      • 替换关联
      • 删除关联
      • 清空关联
      • 关联的数量
  • 预加载
  • 事务
  • Migrate

实现一个关系型数据库应用程序需要做什么?

  • DB API:数据库操作的封装接口。
  • DAO(Data Access Object)Models:数据库 Table(表)、Column(列属性)、Relationship(关联关系)的定义。
  • DB Migrate:数据库的自动化灰度升级、降级实现。

GORM

GORM 是一个 Golang 的第三方库,由国人开源的轻量级关系型 ORM 库。

  • Github:https://github.com/jinzhu/gorm
  • 中文文档:http://gorm.io/zh_CN/
  • Jinzhu 本人的 Topic:https://www.bilibili.com/video/BV1pE411N7Sv
  • V2.0:https://gorm.io/zh_CN/docs/v2_release_note.html

GORM 基于 Golang build-in 的 database/sql 库进行扩展,增加了对面向对象编程更加友好的 ORM 机制。开发者只需要定义好 Struct 数据结构,由 GORM 来完成 Struct 到 ModelStruct 的转换(结构体映射为 Table,结构体成员映射为 Column),并提供了标准的方法进行数据操作,例如:Create、Update、Delete、Where 等

GORM 的特点:

  • 支持 MySQL、PostgreSQL、SQLite、SQLServer 等数据库。
  • 支持 One to One、One to Many、Many To Many、Polymorphism(多态)等多种关联处理类型。
  • 支持钩子(Hook)机制,可以在执行 Create、Save、Update、Delete、Find 操作之前或之后进行回调,具有良好的可扩展性。
  • 支持复合主键:多个属性构成一个主键,常用作索引。
  • 支持自动迁移(Auto-Migrtion)。
  • 支持原生 SQL 操作。
  • 支持链式 API。
  • 支持热加载。
  • 支持事务性。
  • 支持日志。

安装:

go get -u github.com/jinzhu/gorm

连接数据库

要连接到数据库首先要导入相应的驱动程序。GORM 目前支持 4 种关系型数据库:

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"

其实这些库本质是 Golang database/sql 实现的驱动,在 GORM 中取了个别名。需要注意的是,即便不导入这些驱动,也并不影响程序的编译,但却无法正常运行。针对这个 “不确定性” 的实现,在 GORM 2.0 被优化为需要显式传入的实参。

以 PostgreSQL 为例,直接调用 gorm.Open 函数传入 DB URI 即可。

import ("github.com/jinzhu/gorm"_ "github.com/jinzhu/gorm/dialects/postgres"
)func main() {db, err := gorm.Open("postgres", "host=myhost port=myport user=gorm dbname=gorm password=mypassword")defer db.Close()
}

表定义

GORM 的表定义很简单,应用了 Golang 的 Struct Tag 和 Reflect(反射)机制。我们用 “Model Struct” 来描述此类特殊的 ORM Struct。在定义好 Model Struct 之后,直接调用 db.CreateTable 函数就可以创建表了。

基本上:

  • Struct Name 就是 Table Name,表名默认为小写字母复数形式。
  • Struct Members 就是 Table Columns。
  • Struct Tag 使用 gorm:“XXX” 进行 Column 的属性定义,包括:主键、外键、非空等约束。GORM 也会设置缺省的默认属性。

示例:

type Model struct {ID        uint `gorm:"primary_key"`              // id 为主键CreatedAt time.TimeUpdatedAt time.TimeDeletedAt *time.Time `sql:"index"`
}type User struct {gorm.ModelName         stringAge          sql.NullInt64Birthday     *time.TimeEmail        string  `gorm:"type:varchar(100);unique_index"`Role         string  `gorm:"size:255"`              // 设置字段大小为 255MemberNumber *string `gorm:"unique;not null"`       // 设置会员号唯一并且不为空Num          int     `gorm:"AUTO_INCREMENT"`        // 设置 num 为自增类型Address      string  `gorm:"index:addr"`            // 给 address 字段创建名为 addr的 索引IgnoreMe     int     `gorm:"-"`                     // 忽略本字段
}

还可以通过 db.Set 函数设置额外的表属性。例如,设置日志:

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

Module Struct tags

Tag 描述
Column 指定列名
Type 指定列数据类型
Size 指定列大小,默认值 255
PRIMARY_KEY 指定列的主键约束,默认会使用名为 ID 的字段作为表的主键
UNIQUE 指定列的唯一约束
DEFAULT 指定列的默认值
PRECISION 指定列的精度
NOT NULL 指定列的非空约束
AUTO_INCREMENT 指定列为自增类型
INDEX 创建具有或不带名称的索引,如果多个索引同名则创建为复合索引
UNIQUE_INDEX 和 INDEX 类似,只不过创建的是唯一索引
EMBEDDED 将结构设置为嵌入
EMBEDDED_PREFIX 设置嵌入结构的前缀
- 忽略此字段
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 是否自动完成预加载的相关操作

表操作

db.HasTable 表是否存在

// 检查模型 User 的表是否存在
db.HasTable(&User{})// 检查表 users 是否存在
db.HasTable("users")

db.CreateTable 创建表

// 为模型 `User` 创建表
db.CreateTable(&User{})// 创建表时会追加 “ENGINE=InnoDB” 到 SQL 语句中。
db.Set("gorm:table_options", "ENGINE=InnoDB").CreateTable(&User{})

db.DropTable 删除表

// 删除模型 `User` 的表
db.DropTable(&User{})// 删除表 `users`
db.DropTable("users")// 删除模型 `User` 的表和表 `products`
db.DropTableIfExists(&User{}, "products")

db.ModifyColumn 修改列

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

db.DropColumn 删除列

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

db.AddIndex 添加索引

// 为 `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.RemoveIndex 删除索引

// 删除索引
db.Model(&User{}).RemoveIndex("idx_user_name")

db.AddForeignKey 添加外键

// 添加外键
// 第一个参数: 外键字段
// 第二个参数:目标表名(字段)
// 第三个参数:删除时
// 第四个参数: 更新时
db.Model(&User{}).AddForeignKey("city_id", "cities(id)", "RESTRICT", "RESTRICT")

db.RemoveForeignKey 删除外键

db.Model(&User{}).RemoveForeignKey("city_id", "cities(id)")

CRUD 操作

GORM 支持使用链式 API 来完成对 Table 的 CURD 操作,所有链式方法都会创建并克隆一个新的 db 对象 (共享一个连接池),GORM 在多 Goroutine 中是并发安全的。任何时候都可以不断的基于 db 对象调用函数来完成预期的操作,包括调用 db.Error 就能获取到错误信息。

db.Create 插入

调用 db.Create 方法即可完成数据库表记录的插入。需要注意的是,GORM 生成的 SQL 语句会排除零值字段:所有字段的零值,比如 0、’’、false,都不会保存到数据库内。将记录插入到数据库后,Gorm会从数据库加载那些字段的值到变量对象。

var animal = Animal{Age: 99, Name: ""}
db.Create(&animal)
// INSERT INTO animals("age") values('99');
// SELECT name from animals WHERE ID=111; // 返回主键为 111
// animal.Name => 'galeone'

db.First、db.Find 查询

// 根据主键查询第一条记录
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;// 查询所有的记录
db.Find(&users)
SELECT * FROM users;// 查询指定的某条记录(仅当主键为整型时可用)
db.First(&user, 10)
SELECT * FROM users WHERE id = 10;

db.Where 选择

// 获取第一个匹配的记录
db.Where("name = ?", "jinzhu").First(&user)
SELECT * FROM users WHERE name = 'jinzhu' limit 1;// 获取所有匹配的记录
db.Where("name = ?", "jinzhu").Find(&users)
SELECT * FROM users WHERE name = 'jinzhu';// <>
db.Where("name <> ?", "jinzhu").Find(&users)
SELECT * FROM users WHERE name <> 'jinzhu';// IN
db.Where("name IN (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users)
SELECT * FROM users WHERE name in ('jinzhu','jinzhu 2');// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
SELECT * FROM users WHERE name LIKE '%jin%';// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';

当通过结构体进行 WHERE 过滤查询时,GORM 只会通过其中的非零值字段进行查询,例如:0、’’、false 或者其他零值将不会被用于构建查询条件。

// 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);db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)
SELECT * FROM users WHERE name = "jinzhu";
  • db.Or
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';// Struct
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2"}).Find(&users)
SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';// Map
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2"}).Find(&users)
SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';

db.Select 投影

db.Select 指定你想从数据库中检索出的字段,默认会选择全部字段。

db.Select("name, age").Find(&users)
SELECT name, age FROM users;db.Select([]string{"name", "age"}).Find(&users)
SELECT name, age FROM users;db.Table("users").Select("COALESCE(age,?)", 42).Rows()
SELECT COALESCE(age,'42') FROM users;

db.Joins 连接

db.Joins 指定连接条件:

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)

db.Order 排序

db.Order("age desc, name").Find(&users)
SELECT * FROM users ORDER BY age desc, name;// 多字段排序
db.Order("age desc").Order("name").Find(&users)
SELECT * FROM users ORDER BY age desc, name;// 覆盖排序
db.Order("age desc").Find(&users1).Order("age", true).Find(&users2)
SELECT * FROM users ORDER BY age desc; (users1)
SELECT * FROM users ORDER BY age; (users2)

db.Limit 限量

db.Limit 指定从数据库检索出的最大记录数。

db.Limit(3).Find(&users)
SELECT * FROM users LIMIT 3;// -1 取消 Limit 条件
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
SELECT * FROM users LIMIT 10; (users1)
SELECT * FROM users; (users2)

db.Offset 偏移量

db.Offset 指定开始返回记录前要跳过的记录数。

db.Offset(3).Find(&users)
SELECT * FROM users OFFSET 3;// -1 取消 Offset 条件
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
SELECT * FROM users OFFSET 10; (users1)
SELECT * FROM users; (users2)

db.Count 总数

注意:Count 必须是链式查询的最后一个操作 ,因为它会覆盖前面的 SELECT。

db.Where("name = ?", "jinzhu").Or("name = ?", "jinzhu 2").Find(&users).Count(&count)
SELECT * from USERS WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (users)
SELECT count(*) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (count)db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)
SELECT count(*) FROM users WHERE name = 'jinzhu'; (count)db.Table("deleted_users").Count(&count)
SELECT count(*) FROM deleted_users;db.Table("deleted_users").Select("count(distinct(name))").Count(&count)
SELECT count( distinct(name) ) FROM deleted_users; (count)

db.Update 更新

更新所有字段

db.Save 会更新所有字段,即使你没有赋值。

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;

更新修改字段

// 更新单个属性,如果它有变化
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;// 使用 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 更新多个属性,只会更新其中有变化且为非零值的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18})
UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;// 警告:当使用 struct 更新时,GORM只会更新那些非零值的字段
// 对于下面的操作,不会发生任何更新,"", 0, false 都是其类型的零值
db.Model(&user).Updates(User{Name: "", Age: 0, Actived: false})

更新选定字段

如果你想更新或忽略某些字段,你可以使用 Select、Omit:

db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
UPDATE users SET age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

db.Delete 删除

注:删除记录时,一定要确保主键字段有值,否则会删除该 Model 的所有记录。这一点在 GORM 2.0 有了改进,这种情况时会触发错误。

// 删除现有记录
db.Delete(&email)
DELETE from emails where id=10;

软删除

如果 model 有 DeletedAt 字段,那么该 model 具有软删除的功能。当调用 Delete 方法时,记录不会真正的从数据库中被删除,只会将DeletedAt 字段的值会被设置为当前时间,表示 “已删除”。

db.Delete(&user)
UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;// 查询记录时会忽略被软删除的记录
db.Where("age = 20").Find(&user)
SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;// 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;

关系(Relationship)类型

总的来说,现实世界的实体之间拥有 3 种类型的关系:

  • 一对多:表示一对多关系时,在子表中通过外键引用父表。
  • 一对一:表示一对一关系时,本质是在两张表之间建立双向关系。一对一可以看作是特殊的 “一对多”,在双方看来,对方都是一对多的关系。
  • 多对多:表示多对多关系时,表示两张表彼此都可以相互引用对方的多个记录。多对多关系会在两个表之间额外的增加一个关联的表。这个关联表通常只有两个属性,分别为另外两张表的外键。

而 GORM 基于这 3 种关系具现化为了更加实际的类型:Belongs To、Has One、Many To Many 等。在定义 Model Struct 的时,就可以设定这些实体之间的关系,GORM 会自定维护这些关系在数据库中的数据记录,例如:自动填充主键、外键之间的映射、自动维护多对多关联表等。

Belongs To

A belongs to B,表示 A 属于 B,两者是一对一的关系。定义 Belongs To 关系时,A 的外键必须存在,并且是 B 的主键。

type User struct {gorm.ModelName string
}// Profile 属于 User
type Profile struct {gorm.ModelName   stringUserID intUser   User    // 外键
}

使用 db.Related 查找 belongs to 关系:

db.Model(&user).Related(&profile)
SELECT * FROM profiles WHERE user_id = 111; // 111 is user's ID

Has One

A has one B,表示 A “必然” 包含一个 B,两者是一对一的关系。注意,“必然” 意味着 A 总是包含 B 的。B 必须有一个外键,并且是 A 的主键。

// User 只能拥有一张 CreditCard
type User struct {gorm.ModelCreditCard   CreditCard
}type CreditCard struct {gorm.ModelNumber   stringUserID   uint    // 外键
}

通过 db.Related 使用 has one 关联:

var card CreditCard
db.Model(&user).Related(&card, "CreditCard")
SELECT * FROM credit_cards WHERE user_id = 123; // 123 is user's primary key
// CreditCard 是 users 的字段,其含义是,获取 user 的 CreditCard 并填充至 card 变量
// 如果字段名与 model 名相同,比如上面的例子,此时字段名可以省略不写,像这样:
db.Model(&user).Related(&card)

Has Many

A has many B,A 包含了多个 B,两种是一对多的关系。

// User 可以有多张 CreditCards, UserID 是外键
type User struct {gorm.ModelCreditCards []CreditCard
}type CreditCard struct {gorm.ModelNumber   stringUserID  uint    // 外键
}

通过 db.Related 使用 has many 关联:

db.Model(&user).Related(&emails)
SELECT * FROM emails WHERE user_id = 111; // 111 是 user 的主键

Many To Many

Many To Many,多对多关系,需要在两个 model 中添加一张连接表。

比如说,User 和 Language,一个 User 可以说多种 Language,多个 User 也可以说一种 Language,连接表为 user_languages。

type User struct {gorm.ModelLanguages         []*Language `gorm:"many2many:user_languages;"`
}type Language struct {gorm.ModelName stringUsers               []*User     `gorm:"many2many:user_languages;"`
}var users []User
language := Language{}db.First(&language, "id = ?", 111)db.Model(&language).Related(&users,  "Users")
SELECT * FROM "users" INNER JOIN "user_languages" ON "user_languages"."user_id" = "users"."id" WHERE  ("user_languages"."language_id" IN ('111'))

GORM 将为这两个 Struct 创建多对多关系,并且他们的关系将被保存到连接表 user_languages。

db.Model(&user).Related(&languages, "Languages")
SELECT * FROM "languages" INNER JOIN "user_languages" ON "user_languages"."language_id" = "languages"."id" WHERE "user_languages"."user_id" = 111// 查询 user 时会预加载 Languages
db.Preload("Languages").First(&user)

关联(Associations)

关联(Associations)是 GORM 的操作对象,与关系(Relationship)有所区别:

  • 关系:实体之间的关系,社会学用语。
  • 关联:实体之间的联系,工程学用语。
// 开始使用关联模式
var user User
db.Model(&user).Association("Languages")
// `user` 是源,必须包含主键
// `Languages` 是关系中的源的字段名
// 只有在满足上面两个条件时,关联模式才能正常工作,请注意检查错误:
// db.Model(&user).Association("Languages").Error

创建/更新记录时,GORM 将自动保存关联及其引用。如果关联具有主键,GORM 将调用 Update 来保存它,否则将创建它:

user := User{Name:            "jinzhu",BillingAddress:  Address{Address1: "Billing Address - Address 1"},ShippingAddress: Address{Address1: "Shipping Address - Address 1"},Emails:          []Email{{Email: "jinzhu@example.com"},{Email: "jinzhu-2@example.com"},},Languages:       []Language{{Name: "ZH"},{Name: "EN"},},
}db.Create(&user)    // GORM 自动维护数据库中的关联关系。
BEGIN TRANSACTION;
INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1");
INSERT INTO "addresses" (address1) VALUES ("Shipping Address - Address 1");
INSERT INTO "users" (name,billing_address_id,shipping_address_id) VALUES ("jinzhu", 1, 2);
INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu@example.com");
INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu-2@example.com");
INSERT INTO "languages" ("name") VALUES ('ZH');
INSERT INTO user_languages ("user_id","language_id") VALUES (111, 1);
INSERT INTO "languages" ("name") VALUES ('EN');
INSERT INTO user_languages ("user_id","language_id") VALUES (111, 2);
COMMIT;db.Save(&user)

查找关联

db.Model(&user).Association("Languages").Find(&languages)

添加关联

为 Many To Many,Has Many 关系添加一个新的关联,用于替代当前的关联:

db.Model(&user).Association("Languages").Append([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Append(Language{Name: "DE"})

替换关联

db.Model(&user).Association("Languages").Replace([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Replace(Language{Name: "DE"}, languageEN)

删除关联

db.Model(&user).Association("Languages").Delete([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Delete(languageZH, languageEN)

清空关联

db.Model(&user).Association("Languages").Clear()

关联的数量

db.Model(&user).Association("Languages").Count()

预加载

当 Models 之间具有关系时,就可以通过 GORM 的 Preloading(预加载)机制,预先完成多表级联加载,以此来提升访问效率,但同时也会为数据库带来一定的压力。

GORM 默认开启预加载功能,也可以显式的关闭:gorm:"PRELOAD:false

// 下面的例子会用到 User 和 Order 结构体type User struct {gorm.ModelUsername stringOrders Order
}type Order struct {gorm.ModelUserID uintPrice float64
}db.Preload("Orders").Find(&users)
SELECT * FROM users;
SELECT * FROM orders WHERE user_id IN (1,2,3,4);db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
SELECT * FROM users;
SELECT * FROM orders WHERE user_id IN (1,2,3,4) AND state NOT IN ('cancelled');db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
SELECT * FROM users WHERE state = 'active';
SELECT * FROM orders WHERE user_id IN (1,2) AND state NOT IN ('cancelled');db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users)
SELECT * FROM users;
SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many
SELECT * FROM profiles WHERE user_id IN (1,2,3,4); // has one
SELECT * FROM roles WHERE id IN (4,5,6); // belongs to

事务

GORM 默认会将单个的 create, update, delete 操作封装在事务内进行处理,以确保数据的完整性。如果你想把多个 create, update, delete 操作作为一个原子操作,就需要采用事务机制了。

Migrate

Migrate,使你的 Models 保持最新状态。数据库应用程序的 E-R 模型总是演进的,这里就会出现一个需求:怎样在保证原有生产数据完整性的情况下对数据库进行升级,或回滚到之前的某一个时刻以复现环境。

注意,GORM 的自动迁移(Auto-Migrate)只会创建表、缺失的列、缺失的索引,不会更改现有列的类型或删除未使用的列,以此来保护数据的完整性。

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

Go 语言编程 — gorm ORM 框架相关推荐

  1. Go 语言编程 — gorm 数据库版本迁移

    目录 文章目录 目录 前言 AutoMigrate 示例 Migrator 接口(DDL 操作方法) 表操作 列操作 约束操作 索引操作 数据库版本控制 参考文档 前言 本文示例为 GORM V2.0 ...

  2. Go 语言编程 — go-restful RESTful 框架

    目录 文章目录 目录 一个 RESTful API 框架需要什么? go-restful 核心概念 Route WebService Container 过滤器(Filter) 响应编码(Respon ...

  3. Go 语言编程 — gorm 的数据完整性约束

    目录 文章目录 目录 前言 实体完整性(主键约束) 用户定义完整性(非空约束.唯一约束.检查约束和默认值) 参照完整性(外键约束) 关联关系 一对一.一对多关联 多对多关联 示例 前言 本文基于 Po ...

  4. Go 语言编程 — gormigrate GORM 的数据库迁移助手

    目录 文章目录 目录 前言 gormigrate 核心结构体 实现分析 版本定义 InitSchema Migration 版本记录(历史) 版本升级和回退 前言 GORM v2 gormigrate ...

  5. SQLAlchemy ORM框架

    ORM简介 ORM 全称 Object Relational Mapping, 叫对象关系映射.简单的说,ORM 将数据库中的表与面向对象语言中的类建立了一种对应关系.这样,我们要操作数据库,数据库中 ...

  6. 【ORM框架】go语言ORM框架 - gorm快速入门

    文章目录 前言 一.正确看待orm 1.什么是orm 2.常用的orm 3.orm的优缺点 4.如何正确看待orm和sql之间的关系 二.gorm入门 前言 Gorm 是 Golang 的一个 orm ...

  7. go语言ORM框架ent使用教程

    ent是什么 ent是一个简单而又功能强大的Go语言实体框架,ent易于构建和维护应用程序与大数据模型. 简而言之,ent是一款便于操作的orm框架 installation go get entgo ...

  8. C语言实现OOP——轻量级的面向对象 C 语言编程框架 LW_OOPC 介绍(三)

    文章目录 C 语言编程框架 LW_OOPC 介绍(三) 方案的可扩展性如何? LW_OOPC最佳实践 LW_OOPC的优点: LW_OOPC的缺点: 总结: 幕后花絮: 参考资料: C 语言编程框架 ...

  9. C语言实现OOP——轻量级的面向对象 C 语言编程框架 LW_OOPC 介绍(二)

    轻量级的面向对象 C 语言编程框架 LW_OOPC 介绍 下面,再举一个稍微复杂的例子,它的覆盖面是足够全面的,足以一瞥面向对象编程的3个要素:数据抽象.继承和多态.通过这个例子,我们期望展现出LW_ ...

最新文章

  1. Android textview 只显示一行,多余部分显示.....
  2. leftjoin及多个leftjoin执行顺序
  3. git错误提交怎么整?强行回滚最为致命
  4. JVM-04垃圾收集Garbage Collection(上)【垃圾对象的判定】
  5. CodeForces - 613D Kingdom and its Cities(虚树+贪心)
  6. html嵌入audio格式不支持,html5中audio支持音频格式的解决方法
  7. 条目十四《使用reserve来避免不必要的重新分配》
  8. 30岁女IT工程师感叹:靠这工具,把报表做成养老工作,月薪快3W
  9. Linux brctl 命令,虚拟网络设备 LinuxBridge 管理工具
  10. 机器学习 --- 概率图 - 概述
  11. H.264/AVC 的各大主流编解码器
  12. C#打印机套打三联单
  13. 对接支付宝APP支付后端实现最详细教程
  14. 虚幻4连接mysql蓝图教程_ue4商城资源MySQL Integration数据库集成插件
  15. android_rooting_tools 项目介绍(CVE-2012-4220)
  16. 【网络】OAuth2和JWT
  17. 学生管理系统(添加、查看、删除、修改学生信息)
  18. 基于Wio Terminal的简易智能家居中控系统(初代版本)
  19. 无80和443端口下申请域名SSL证书(适用于 acme.sh 和 certbot)
  20. 自学K60时的部分小结

热门文章

  1. Nmap扫描教程之DNS服务类
  2. 初识html语言,初识 “HTML”
  3. mysql innodbuffer修改_mysql参数之innodb_buffer_pool_size大小设置
  4. iOS将数字转成货币格式字符串
  5. oracle中treat函数,Oracle 函数概览
  6. libnet发包java语言_libnet-1.1.2.1
  7. letswave7中文教程2:脑电数据预处理-通道位置分配
  8. 漫威游戏的VR体验 让你尖叫
  9. “众所周知,视频不能P”,GAN:是吗?
  10. 你的iPhone 13,粉了吗?