特性

  • 全功能 ORM
  • 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
  • Create,Save,Update,Delete,Find 中钩子方法
  • 支持 PreloadJoins 的预加载
  • 事务,嵌套事务,Save Point,Rollback To Saved Point
  • Context、预编译模式、DryRun 模式
  • 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
  • SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
  • 复合主键,索引,约束
  • Auto Migration
  • 自定义 Logger
  • 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
  • 每个特性都经过了测试的重重考验
  • 开发者友好

安装

中文文档:https://gorm.io/zh_CN/docs/index.html

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

连接mysql

方式一(不推荐)

import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
) func main() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}

方式二(推荐)

db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
// DSN: "name:password@tcp(127.0.0.1:3306)/databasename?charset=utf8mb4&parseTime=True&loc=Local",
DefaultStringSize: 256, // string 类型字段的默认长度
// 如果是utf8mb4 DefaultStringSize: 171
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}), &gorm.Config{
DisableNestedTransaction: true, // 关闭嵌套 事务
})

配置

type Config struct {
SkipDefaultTransaction bool
NamingStrategy schema.Namer
Logger logger.Interface
NowFunc func() time.Time
DryRun bool
PrepareStmt bool
DisableNestedTransaction bool
AllowGlobalUpdate bool
DisableAutomaticPing bool
DisableForeignKeyConstraintWhenMigrating bool
}

跳过默认事务

为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,您可以在初始化时禁用它

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
SkipDefaultTransaction: true,
})

命名策略

GORM 允许用户通过覆盖默认的NamingStrategy来更改命名约定,这需要实现接口 Namer

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
TablePrefix: "t_", // 表名前缀
SingularTable: true, // use singular table name, table for `User` would be `user` with this option enabled
NoLowerCase: true, // 表面加s
NameReplacer: strings.NewReplacer("CID", "Cid"), // use name replacer to change struct/field name before convert it to db name
},
})

声明模型

声明 model 时,tag 是可选的,GORM 支持以下 tag: tag 名大小写不敏感,但建议使用 camelCase 风格

标签名 说明
column 指定 db 列名
type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT
serializer 指定将数据序列化或反序列化到数据库中的序列化器, 例如: serializer:json/gob/unixtime
size 定义列数据类型的大小或长度,例如 size: 256
primaryKey 将列定义为主键
unique 将列定义为唯一键
default 定义列的默认值
precision 指定列的精度
scale 指定列大小
not null 指定列为 NOT NULL
autoIncrement 指定列为自动增长
autoIncrementIncrement 自动步长,控制连续记录之间的间隔
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 无读权限
- 忽略该字段,- 表示无读写,-:migration 表示无迁移权限,-:all 表示无读写迁移权限

模型定义

模型是标准的 struct,由 Go 的基本数据类型、实现了 ScannerValuer 接口的自定义类型及其指针或别名组成

type User struct {
ID uint
Name string
Email *string
Age uint8
Birthday *time.Time
MemberNumber sql.NullString
ActivatedAt sql.NullTime
CreatedAt time.Time
UpdatedAt time.Time
}

创建/更新时间追踪

GORM 约定使用 CreatedAtUpdatedAt 追踪创建/更新时间。如果您定义了这种字段,GORM 在创建、更新时会自动填充 当前时间

要使用不同名称的字段,您可以配置 autoCreateTimeautoUpdateTime 标签

如果您想要保存 UNIX(毫/纳)秒时间戳,而不是 time,您只需简单地将 time.Time 修改为 int 即可

type User struct {
CreatedAt time.Time // 在创建时,如果该字段值为零值,则使用当前时间填充
UpdatedAt int // 在创建时该字段值为零值或者在更新时,使用当前时间戳秒数填充
Updated int64 `gorm:"autoUpdateTime:nano"` // 使用时间戳填纳秒数充更新时间
Updated int64 `gorm:"autoUpdateTime:milli"` // 使用时间戳毫秒数填充更新时间
Created int64 `gorm:"autoCreateTime"` // 使用时间戳秒数填充创建时间
}

字段级权限控制

GORM 允许您用标签控制字段级别的权限。这样您就可以让一个字段的权限是只读、只写、只创建、只更新或者被忽略

type User struct {
Name string `gorm:"<-:create"` // 允许读和创建
Name string `gorm:"<-:update"` // 允许读和更新
Name string `gorm:"<-"` // 允许读和写(创建和更新)
Name string `gorm:"<-:false"` // 允许读,禁止写
Name string `gorm:"->"` // 只读(除非有自定义配置,否则禁止写)
Name string `gorm:"->;<-:create"` // 允许读和写
Name string `gorm:"->:false;<-:create"` // 仅创建(禁止从 db 读)
Name string `gorm:"-"` // 通过 struct 读写会忽略该字段
Name string `gorm:"-:all"` // 通过 struct 读写、迁移会忽略该字段
Name string `gorm:"-:migration"` // 通过 struct 迁移会忽略该字段
}

嵌入结构体

ype Author struct {
Name string
Email string
} type Blog struct {
ID int
Author Author `gorm:"embedded"`
Upvotes int32
}
// 等效于
type Blog struct {
ID int64
Name string
Email string
Upvotes int32
}
type Blog struct {
ID int
Author Author `gorm:"embedded;embeddedPrefix:author_"`
Upvotes int32
}
// 等效于
type Blog struct {
ID int64
AuthorName string
AuthorEmail string
Upvotes int32
}

迁移

AutoMigrate

AutoMigrate 用于自动迁移您的 schema,保持您的 schema 是最新的。

db.AutoMigrate(&User{})

db.AutoMigrate(&User{}, &Product{}, &Order{})

执行 或者文件即可迁移

type User struct {
Id int `gorm:"primaryKey"`
Name string `gorm:"index"`
Password string
}
type Good struct {
Id int `gorm:"primaryKey;autoIncrement"`
Name string `gorm:"index;not null;default:test"`
}
_ = db.AutoMigrate(&User{},&Good{})

Migrator 接口

GORM 提供了 Migrator 接口,该接口为每个数据库提供了统一的 API 接口,可用来为您的数据库构建独立迁移,例如:

SQLite 不支持 ALTER COLUMNDROP COLUMN,当你试图修改表结构,GORM 将创建一个新表、复制所有数据、删除旧表、重命名新表。

一些版本的 MySQL 不支持 rename 列,索引。GORM 将基于您使用 MySQL 的版本执行不同 SQL

type Migrator interface {
// AutoMigrate 自动迁移
AutoMigrate(dst ...interface{}) error // Database 数据库相关
CurrentDatabase() string
FullDataTypeOf(*schema.Field) clause.Expr // Tables 表相关
CreateTable(dst ...interface{}) error
DropTable(dst ...interface{}) error
HasTable(dst interface{}) bool
RenameTable(oldName, newName interface{}) error
GetTables() (tableList []string, err error) // Columns 列相关
AddColumn(dst interface{}, field string) error
DropColumn(dst interface{}, field string) error
AlterColumn(dst interface{}, field string) error
MigrateColumn(dst interface{}, field *schema.Field, columnType ColumnType) error
HasColumn(dst interface{}, field string) bool
RenameColumn(dst interface{}, oldName, field string) error
ColumnTypes(dst interface{}) ([]ColumnType, error) // Constraints
CreateConstraint(dst interface{}, name string) error
DropConstraint(dst interface{}, name string) error
HasConstraint(dst interface{}, name string) bool // Indexes 索引相关
CreateIndex(dst interface{}, name string) error
DropIndex(dst interface{}, name string) error
HasIndex(dst interface{}, name string) bool
RenameIndex(dst interface{}, oldName, newName string) error
}

数据库

返回当前数据库名称

db.Migrator().CurrentDatabase()

表操作

// 为 `User` 创建表
db.Migrator().CreateTable(&User{}) // 将 "ENGINE=InnoDB" 添加到创建 `User` 的 SQL 里去
db.Set("gorm:table_options", "ENGINE=InnoDB").Migrator().CreateTable(&User{}) // 检查 `User` 对应的表是否存在
db.Migrator().HasTable(&User{})
db.Migrator().HasTable("users") // 如果存在表则删除(删除时会忽略、删除外键约束)
db.Migrator().DropTable(&User{})
db.Migrator().DropTable("users") // 重命名表
db.Migrator().RenameTable(&User{}, &UserInfo{})
db.Migrator().RenameTable("users", "user_infos")

列操作

type User struct {
Name string
} // 添加 name 字段
db.Migrator().AddColumn(&User{}, "Name")
// 删除 name 字段
db.Migrator().DropColumn(&User{}, "Name")
// 修改 name 字段
db.Migrator().AlterColumn(&User{}, "Name")
// 检查 name 字段是否存在
db.Migrator().HasColumn(&User{}, "Name") type User struct {
Name string
NewName string
} // 字段重命名
db.Migrator().RenameColumn(&User{}, "Name", "NewName")
db.Migrator().RenameColumn(&User{}, "name", "new_name") // 字段类型
db.Migrator().ColumnTypes(&User{}) ([]gorm.ColumnType, error) type ColumnType interface {
Name() string
DatabaseTypeName() string // varchar
ColumnType() (columnType string, ok bool) // varchar(64)
PrimaryKey() (isPrimaryKey bool, ok bool)
AutoIncrement() (isAutoIncrement bool, ok bool)
Length() (length int64, ok bool)
DecimalSize() (precision int64, scale int64, ok bool)
Nullable() (nullable bool, ok bool)
Unique() (unique bool, ok bool)
ScanType() reflect.Type
Comment() (value string, ok bool)
DefaultValue() (value string, ok bool)
}

约束操作

type UserIndex struct {
Name string `gorm:"check:name_checker,name <> 'jinzhu'"`
} // 创建约束
db.Migrator().CreateConstraint(&User{}, "name_checker") // 删除约束
db.Migrator().DropConstraint(&User{}, "name_checker") // 检查约束是否存在
db.Migrator().HasConstraint(&User{}, "name_checker")

为 relation 创建外键

type User struct {
gorm.Model
CreditCards []CreditCard
} type CreditCard struct {
gorm.Model
Number string
UserID uint
} // 为 user & credit_cards 创建 db 外键
db.Migrator().CreateConstraint(&User{}, "CreditCards")
db.Migrator().CreateConstraint(&User{}, "fk_users_credit_cards")
// ALTER TABLE `credit_cards` ADD CONSTRAINT `fk_users_credit_cards` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) // 检查 user & credit_cards 的外键是否存在
db.Migrator().HasConstraint(&User{}, "CreditCards")
db.Migrator().HasConstraint(&User{}, "fk_users_credit_cards") // 删除 user & credit_cards 的 db 外键
db.Migrator().DropConstraint(&User{}, "CreditCards")
db.Migrator().DropConstraint(&User{}, "fk_users_credit_cards")

索引操作

type User struct {
gorm.Model
Name string `gorm:"size:255;index:idx_name,unique"`
} // 为 Name 字段创建索引
db.Migrator().CreateIndex(&User{}, "Name")
db.Migrator().CreateIndex(&User{}, "idx_name") // 为 Name 字段删除索引
db.Migrator().DropIndex(&User{}, "Name")
db.Migrator().DropIndex(&User{}, "idx_name") // 检查索引是否存在
db.Migrator().HasIndex(&User{}, "Name")
db.Migrator().HasIndex(&User{}, "idx_name") type User struct {
gorm.Model
Name string `gorm:"size:255;index:idx_name,unique"`
Name2 string `gorm:"size:255;index:idx_name_2,unique"`
}
// 修改索引名
db.Migrator().RenameIndex(&User{}, "Name", "Name2")
db.Migrator().RenameIndex(&User{}, "idx_name", "idx_name_2")

约束操作

GORM 会在自动迁移或建表时创建约束,请参阅 约束数据库索引 以了解详情

其他迁移工具

GORM 的 AutoMigrate 适用于大多数的迁移,如果您需要更加个性化的迁移工具 ,GORM 提供的一个通用数据库接口可能对您有帮助。

// returns `*sql.DB`
db.DB()

CRUD

创建

type User struct {
Id int `gorm:"primaryKey"`
Name string `gorm:"index;unique;not null"`
Password string `gorm:"not null"`
} user := User{Name: "杨玉财", Password: "123"}
res := db.Create(&user)

指定的字段创建

创建记录并更新给出的字段。

db.Select("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775")

创建一个记录且一同忽略传递给略去的字段值。

db.Omit("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775")

批量插入

userList := []User{{Name: "何二蛋", Password: "123"}, {Name: "傻吊", Password: "123"}}
res := db.Create(&userList)

使用 CreateInBatches 分批创建时,你可以指定每批的数量,例如:

var users = []User{{name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}

// 数量为 100
db.CreateInBatches(users, 100)

查询

单个对象

GORM 提供了 FirstTakeLast 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1 条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误

type User struct {
Id int `gorm:"primaryKey"`
Name string `gorm:"index;unique;not null"`
Password string `gorm:"not null"`
Phone string `gorm:"Size:11"`
} var user User
// 获取第一条记录(主键升序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;
fmt.Println(&user) // 获取一条记录,没有指定排序字段
db.Take(&user)
// SELECT * FROM users LIMIT 1;
fmt.Println(&user) // 获取最后一条记录(主键降序)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;
fmt.Println(&user) result := db.First(&user)
result.RowsAffected // 返回找到的记录数
result.Error // returns error or nil // 检查 ErrRecordNotFound 错误
errors.Is(result.Error, gorm.ErrRecordNotFound)

First 和 Last 会根据主键排序,分别查询第一条和最后一条记录。 只有在目标 struct 是指针或者通过 db.Model() 指定 model 时,该方法才有效。 此外,如果相关 model 没有定义主键,那么将按 model 的第一个字段进行排序。 例如:First 和 Last 会根据主键排序,分别查询第一条和最后一条记录。 只有在目标 struct 是指针或者通过 db.Model() 指定 model 时,该方法才有效。 此外,如果相关 model 没有定义主键,那么将按 model 的第一个字段进行排序。 例如:

var user User
var users []User // works because destination struct is passed in
db.First(&user)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1 // works because model is specified using `db.Model()`
result := map[string]interface{}{}
db.Model(&User{}).First(&result)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1 // doesn't work
result := map[string]interface{}{}
db.Table("users").First(&result) // works with Take
result := map[string]interface{}{}
db.Table("users").Take(&result) // no primary key defined, results will be ordered by first field (i.e., `Code`)
type Language struct {
Code string
Name string
}
db.First(&Language{})
// SELECT * FROM `languages` ORDER BY `languages`.`code` LIMIT 1

根据主键检索

如果主键是数字类型,您可以使用 内联条件 来检索对象。 传入字符串参数时,需要特别注意 SQL 注入问题,查看 安全 获取详情.

db.First(&user, 10)
// SELECT * FROM users WHERE id = 10; db.First(&user, "10")
// SELECT * FROM users WHERE id = 10; var users []User
db.Find(&users, []int{1,2,3})
// SELECT * FROM users WHERE id IN (1,2,3);

如果主键是字符串(例如像 uuid),查询将被写成这样:

db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")
// SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";

当目标对象有一个主要值时,将使用主键构建条件,例如:

var user = User{ID: 10}
db.First(&user)
// SELECT * FROM users WHERE id = 10; var result User
db.Model(User{ID: 10}).First(&result)
// SELECT * FROM users WHERE id = 10;

条件查询

where
// Get first matched record
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1; // Get all matched records
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'; // 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; // Slice of primary keys
db.Where([]int64{20, 21, 22}).Find(&users)
// SELECT * FROM users WHERE id IN (20, 21, 22); // NOTE 当使用struct查询时,GORM只对非零字段进行查询,也就是说如果你的字段的值是0,'',false或其他零值,它将不会被用来建立查询条件,例如: db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu"; db.Where(map[string]interface{}{"Name": "jinzhu", "Age": 0}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;

查询条件也可以被内联到 FirstFind 之类的方法中,其用法类似于 Where

// Get by primary key if it were a non-integer type
db.First(&user, "id = ?", "string_primary_key")
// SELECT * FROM users WHERE id = 'string_primary_key'; // Plain SQL
db.Find(&user, "name = ?", "jinzhu")
// SELECT * FROM users WHERE name = "jinzhu"; db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20; // Struct
db.Find(&users, User{Age: 20})
// SELECT * FROM users WHERE age = 20; // Map
db.Find(&users, map[string]interface{}{"age": 20})
// SELECT * FROM users WHERE age = 20;
Not
db.Not("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE NOT name = "jinzhu" ORDER BY id LIMIT 1; // Not In
db.Not(map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}).Find(&users)
// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2"); // Struct
db.Not(User{Name: "jinzhu", Age: 18}).First(&user)
// SELECT * FROM users WHERE name <> "jinzhu" AND age <> 18 ORDER BY id LIMIT 1; // Not In slice of primary keys
db.Not([]int64{1,2,3}).First(&user)
// SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;
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", Age: 18}).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18); // Map
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2", "age": 18}).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);
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;
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; db.Clauses(clause.OrderBy{
Expression: clause.Expr{SQL: "FIELD(id,?)", Vars: []interface{}{[]int{1, 2, 3}}, WithoutParentheses: true},
}).Find(&User{})
// SELECT * FROM users ORDER BY FIELD(id,1,2,3)
Joins
指定join条件

type result struct {
Name string
Email string
} db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
// SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id 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) // multiple joins with parameter
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)
智能选择返回字段
type User struct {
ID uint
Name string
Gender string
// 假设后面还有几百个字段...
} type APIUser struct {
ID uint
Name string
} var apiUser APIUser db.Model(&User{}).First(&apiUser)

更新

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{}).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true; // User 的 ID 是 `111`
db.Model(&user).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111; // 根据条件和 model 的值进行更新
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

更新多列

// 根据 `struct` 更新属性,只会更新非零值的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111; // 根据 `map` 更新属性
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

删除

// Email 的 ID 是 `10`
db.Delete(&email)
// DELETE from emails where id = 10; // 带额外条件的删除
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE from emails where id = 10 AND name = "jinzhu";
db.Delete(&User{}, 10)
// DELETE FROM users WHERE id = 10; db.Delete(&User{}, "10")
// DELETE FROM users WHERE id = 10; db.Delete(&users, []int{1,2,3})
// DELETE FROM users WHERE id IN (1,2,3);

最新文章

  1. 安卓ApiDemos最简单的使用方法
  2. FlexBox标准及兼容写法速查表
  3. 理解 Linux 网络栈(2):非虚拟化Linux 环境中的 Segmentation Offloading 技术
  4. angularjs学习笔记—工具方法
  5. map find 是线程安全的吗
  6. Linux_CentOS6.5安装vncserver实现图形化访问
  7. IOS第12天(4,作业弹出键盘上加上(xib)view的处理,时间选择,代理模式使用,键盘的监听 )
  8. Linux多节点互信配置
  9. SqlServer常用语句参考
  10. pbfunc外部函数扩展应用-在Powerbuilder中进行Http的GET、POST操作
  11. php 添加redis扩展(二)
  12. 美版nexus 5 LG D820才支持CDMA,国际版LG D821不支持
  13. 给工程师的 10 条哲理(浅薄者迷信运气,强者相信因果,软件复制成本为零,文凭不重要,AWS使得创业成本为零,每个手机都是口袋里的强大电脑)
  14. ace_tree总结。各类问题解决办法汇集
  15. [转]Python 资源大全中文版
  16. 苹果电脑利用curl下载数据集
  17. Python3基础 list pop 取出列表的最后一个元素
  18. 用原生js来处理跨域的数据(jsonp)
  19. nodejs通过request请求远程url的文件并下载到本地
  20. PHP 操作Mongodb 实例

热门文章

  1. .net core + eureka + spring boot 服务注册与调用
  2. 理解C++函数指针和指针函数(一)
  3. SpringMvc请求流程源码解析
  4. 性能调优——小小的log大大的坑
  5. JS数据结构之 Map
  6. 我的Go gRPC之旅、01 初识gRPC,感受gRPC的强大魅力
  7. 《吐血整理》高级系列教程-吃透Fiddler抓包教程(22)-如何使用Fiddler生成Jmeter脚本-下篇
  8. nexus迁移
  9. 03_配置Java环境变量
  10. HDU1114 Piggy-Bank (完全背包)