GORM(1)-入门指南

一、概述

1.1 特性

  • 全功能ORM(几乎)
  • 关联(包含一个,包含多个,属于,多对多,多种包含)
  • Callbacks(创建/保存/更新/删除/查找之前/之后)
  • 预加载(急加载)
  • 事务
  • 复合主键
  • SQL Builder
  • 自动迁移
  • 日志
  • 可扩展,编写基于GORM回调的插件
  • 每个功能都有测试
  • 开发人员友好

1.2 安装

1go get -u github.com/jinzhu/gorm

1.3 快速入门

 1package main
 2
 3import (
 4  "github.com/jinzhu/gorm"
 5  _ "github.com/jinzhu/gorm/dialects/sqlite"
 6)
 7
 8type Product struct {
 9  gorm.Model
10  Code string
11  Price uint
12}
13
14func main() {
15  db, err := gorm.Open("sqlite3", "test.db")
16  if err != nil {
17    panic("failed to connect database")
18  }
19  defer db.Close()
20
21  // Migrate the schema
22  db.AutoMigrate(&Product{})
23
24  // 创建
25  db.Create(&Product{Code: "L1212", Price: 1000})
26
27  // 读取
28  var product Product
29  db.First(&product, 1) // 查询id为1的product
30  db.First(&product, "code = ?", "L1212") // 查询code为l1212的product
31
32  // 更新 - 更新product的price为2000
33  db.Model(&product).Update("Price", 2000)
34
35  // 删除 - 删除product
36  db.Delete(&product)
37}

二、模型定义

2.1 模型定义

模型(Models)通常只是正常的 golang structs、基本的 go 类型或它们的指针。 同时也支持sql.Scannerdriver.Valuer 接口(interfaces)。

模型(Model)示例:

 1type User struct {
 2  gorm.Model
 3  Name         string
 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.2 结构体标记(tags)

标记(tags)在声明模型时是可选项。gorm 支持以下标记:

支持的结构体标记(Struct tags)

结构体标记(Tag) 描述
Column 指定列名
Type 指定列数据类型
Size 指定列大小, 默认值255
PRIMARY_KEY 将列指定为主键
UNIQUE 将列指定为唯一
DEFAULT 指定列默认值
PRECISION 指定列精度
NOT NULL 将列指定为非 NULL
AUTO_INCREMENT 指定列是否为自增类型
INDEX 创建具有或不带名称的索引, 如果多个索引同名则创建复合索引
UNIQUE_INDEX INDEX 类似,只不过创建的是唯一索引
EMBEDDED 将结构设置为嵌入
EMBEDDED_PREFIX 设置嵌入结构的前缀
- 忽略此字段

关联 struct 的标记(tags)

关联 struct 的详情请查看关联部分

结构体标记(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 是否自动完成预加载的相关操作

三、约定

3.1 gorm.Model

gorm.Model 是一个包含了ID, CreatedAt, UpdatedAt, DeletedAt四个字段的GoLang结构体。

你可以将它嵌入到你自己的模型中,当然你也可以完全使用自己的模型。

 1// gorm.Model 定义
 2type Model struct {
 3  ID        uint `gorm:"primary_key"`
 4  CreatedAt time.Time
 5  UpdatedAt time.Time
 6  DeletedAt *time.Time
 7}
 8
 9// Inject fields `ID`, `CreatedAt`, `UpdatedAt`, `DeletedAt` into model `User`
10// 将 `ID`, `CreatedAt`, `UpdatedAt`, `DeletedAt`字段注入到`User`模型中
11type User struct {
12  gorm.Model
13  Name string
14}
15
16// 不使用gorm.Model定义模型
17type User struct {
18  ID   int
19  Name string
20}

3.2 ID 作为主键

GORM 默认会使用名为ID的字段作为表的主键。

 1type User struct {
 2  ID   string // 名为`ID`的字段会默认作为表的主键
 3  Name string
 4}
 5
 6// 使用`AnimalID`作为主键
 7type Animal struct {
 8  AnimalID int64 `gorm:"primary_key"`
 9  Name     string
10  Age      int64
11}

3.3 表名(Table Name)

表名默认就是结构体名称的复数,例如:

 1type User struct {} // 默认表名是 `users`
 2
 3// 将 User 的表名设置为 `profiles`
 4func (User) TableName() string {
 5  return "profiles"
 6}
 7
 8func (u User) TableName() string {
 9  if u.Role == "admin" {
10    return "admin_users"
11  } else {
12    return "users"
13  }
14}
15
16// 禁用默认表名的复数形式,如果置为 true,则 `User` 的默认表名是 `user`
17db.SingularTable(true)

指定表名称

1// 使用User结构体创建名为`deleted_users`的表
2db.Table("deleted_users").CreateTable(&User{})
3
4var deleted_users []User
5db.Table("deleted_users").Find(&deleted_users)
6//// SELECT * FROM deleted_users;
7
8db.Table("deleted_users").Where("name = ?", "jinzhu").Delete()
9//// DELETE FROM deleted_users WHERE name = 'jinzhu';

更改默认表名称(table name)

你可以通过定义DefaultTableNameHandler来设置默认表名的命名规则

1gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string) string  {
2  return "prefix_" + defaultTableName;
3}

3.4 下划线分割命名(Snake Case)的列名

列名由字段名称进行下划线分割来生成

 1type User struct {
 2  ID        uint      // column name is `id`
 3  Name      string    // column name is `name`
 4  Birthday  time.Time // column name is `birthday`
 5  CreatedAt time.Time // column name is `created_at`
 6}
 7
 8// Overriding Column Name
 9type Animal struct {
10  AnimalId    int64     `gorm:"column:beast_id"`         // set column name to `beast_id`
11  Birthday    time.Time `gorm:"column:day_of_the_beast"` // set column name to `day_of_the_beast`
12  Age         int64     `gorm:"column:age_of_the_beast"` // set column name to `age_of_the_beast`
13}

3.5 时间点(Timestamp)跟踪

CreatedAt

如果模型有 CreatedAt字段,该字段的值将会是初次创建记录的时间。

1db.Create(&user) // `CreatedAt`将会是当前时间
2
3// 可以使用`Update`方法来改变`CreateAt`的值
4db.Model(&user).Update("CreatedAt", time.Now())

UpdatedAt

如果模型有UpdatedAt字段,该字段的值将会是每次更新记录的时间。

1db.Save(&user) // `UpdatedAt`将会是当前时间
2
3db.Model(&user).Update("name", "jinzhu") // `UpdatedAt`将会是当前时间

DeletedAt

如果模型有DeletedAt字段,调用Delete删除该记录时,将会设置DeletedAt字段为当前时间,而不是直接将记录从数据库中删除。

四、连接数据库

4.1 连接数据库

想要连接数据库,你需要先导入对应数据库的驱动,比如:

1import _ "github.com/go-sql-driver/mysql"

GORM 已经包装了一些驱动,以便更容易的记住导入路径,所以你可以这样导入 mysql 的驱动:

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

4.2 所支持的数据库

MySQL

注意:

想要能正确的处理 time.Time,你需要添加 parseTime 参数。 (更多支持的参数)

想要完全的支持 UTF-8 编码,你需要修改charset=utf8charset=utf8mb4。 详情请查看 utf8mb4.

1import (
2  "github.com/jinzhu/gorm"
3  _ "github.com/jinzhu/gorm/dialects/mysql"
4)
5
6func main() {
7  db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local")
8  defer db.Close()
9}

如果你想指定主机,你需要使用 (). 例如:

1user:password@(localhost)/dbname?charset=utf8&parseTime=True&loc=Local

PostgreSQL

1import (
2  "github.com/jinzhu/gorm"
3  _ "github.com/jinzhu/gorm/dialects/postgres"
4)
5
6func main() {
7  db, err := gorm.Open("postgres", "host=myhost port=myport user=gorm dbname=gorm password=mypassword")
8  defer db.Close()
9}

Sqlite3

注意: 你也可以使用 :memory: 替换一个文件路径。 这会告诉 sqlite 使用内存作为一个临时数据。 当你针对 GORM 应用进行测试时,这特别有用,因为你的测试需要一个真正的数据库,并且该数据库位于内存中,性能也很好。

1import (
2  "github.com/jinzhu/gorm"
3  _ "github.com/jinzhu/gorm/dialects/sqlite"
4)
5
6func main() {
7  db, err := gorm.Open("sqlite3", "/tmp/gorm.db")
8  defer db.Close()
9}

SQL Server

Get started with SQL Server, 它可以运行在你的 MacLinux 和 Docker

1import (
2  "github.com/jinzhu/gorm"
3  _ "github.com/jinzhu/gorm/dialects/mssql"
4)
5
6func main() {
7  db, err := gorm.Open("mssql", "sqlserver://username:password@localhost:1433?database=dbname")
8  defer db.Close()
9}

4.3 不支持的数据库

GORM 官方支持上述四个数据库,您可以为不受支持的数据库编写dialects。