mysql之GORM接口

Mysqlgolang中推荐使用Gormhttps://gorm.io/docs/

import "github.com/jinzhu/gorm"

在 MySQL 中,表有两种引擎,一种是 InnoDB ,另外一种是 myisam 。如果使用的是 InnoDB 引擎,是支持外键约束的。外键的存在使得 ORM 框架在处理表关系的时候异常的强大。MySQL中“键”和“索引”的定义相同,所以外键和主键一样也是索引的一种。不同的是MySQL会自动为所有表的主键进行索引,但是外键字段必须由用户进行明确的索引。用于外键关系的字段必须在所有的参照表中进行明确地索引,InnoDB不能自动地创建索引。MySQL外键的作用:保持数据一致性,完整性,主要目的是控制存储在外键表中的数据,使两张表形成关联,外键只能引用外表中列的值。

1. 连接数据库

user:password@(localhost)/dbname?charset=utf8&parseTime=True&loc=Local
db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local")
defer db.Close()
db.SingularTable(true)
db.LogMode(true)
db.DB().SetMaxIdleConns(10)
db.DB().SetMaxOpenConns(100)

In order to handle time.Time correctly, you need to include parseTime as a parameter.

In order to fully support UTF-8 encoding, you need to change charset=utf8 to charset=utf8mb4.

2. model

由于gorm是使用的orm映射,所以需要定义要操作的表的model,在go中需要定义一个struct, struct的名字就是对应数据库中的表名注意gorm查找struct名对应数据库中的表名的时候会默认把你的struct中的大写字母转换为小写并加上“s”,所以可以加上 db.SingularTable(true) 让gorm转义struct名字的时候不用加上s。可以提前在数据库中创建好表的然后再用gorm去查询的,也可以用gorm去创建表,建议直接在数据库上创建,修改表字段的操作方便,gorm只用来查询和更新数据。

type User struct {
  gorm.Model //嵌入常用字段
  Name         string
  Age          sql.NullInt64
  Birthday     *time.Time
  Email        string  `gorm:"type:varchar(100);unique_index"`
  Role         string  `gorm:"size:255"` // set field size to 255
  MemberNumber *string `gorm:"unique;not null"` // set member number to unique and not null
  Num          int     `gorm:"AUTO_INCREMENT"` // set num to auto incrementable
  Address      string  `gorm:"index:addr"` // create index with name `addr` for address
  IgnoreMe     int     `gorm:"-"` // ignore this field
}

* tag(如:gorm、json form)后面的冒号与双引号之间不能有空格;
* 同一种tag,不同属性定义使用“;”分隔;不同tag 使用空格“ ”分隔;
* 使用预加载Preload,需要在结构体中指明外键(不指名则默认是预加载表的主键作为外键)

定义models时tags是可选项,gorm支持如下tags:

Column:指定列名

Type:指定列数据类型

Size:指定列大小,默认255

PRIMARY_KEY:指定列作为主键

UNIQUE:指定列唯一

DEFAULT:指定列默认值

PRECISION:指定列精度

NOT NULL:指定列NOT NULL

AUTO_INCREMENT:指定列自动增加

INDEX:创建索引with or without name,same name creates composite indexes

UNIQUE_INDEX:Like INDEX,Create unique index

EMBEDDED:Set struct as embedded

EMBEDDED_PREFIX:Set embedded struct’s prefix name

-:忽略此域fields

gorm.Model

gorm.Model is a basic GoLang struct which includes the following fields: ID, CreatedAt, UpdatedAt, DeletedAt. 

It may be embedded into your model or you may build your own model without it.

// gorm.Model definition
type Model struct {
  ID        uint `gorm:"primary_key"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt *time.Time
}

// Inject fields `ID`, `CreatedAt`, `UpdatedAt`, `DeletedAt` into model `User`
type User struct {
  gorm.Model
  Name string
}

// Declaring model w/o gorm.Model
type User struct {
  ID   int
  Name string
}

GORM uses any field with the name ID as the table’s primary key by default.

type User struct {
  ID   string // field named `ID` will be used as primary field by default
  Name string
}

// Set field `AnimalID` as primary field
type Animal struct {
  AnimalID int64 `gorm:"primary_key"`
  Name     string
  Age      int64
}

You can apply any rules on the default table name by defining the DefaultTableNameHandler.

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

3. 增删改查

注:域的零值不会被插入数据库,也不会被查询,但使用时的值为默认零值。

all fields having a zero value, like 0, '', false or other zero values, won’t be saved into the database but will use its default value. If you want to avoid this, consider using a pointer type or scanner/valuer.

When query with struct, GORM will only query with those fields has non-zero value, that means if your field’s value is 0, '', false or other zero values, it won’t be used to build query conditions, for example:

db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)
//// SELECT * FROM users WHERE name = "jinzhu";

Struct& map查询

// 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);

示例:

func BatchSave(db *gorm.DB, users []User) error{
        var buffer bytes.Buffer
        sql := "insert into `VIP_USER` (`birthday`, `age`, `name`, `email`, `description`) values"
        if _, err := buffer.WriteString(sql); err != nil {
                return err
        }
        for i, u := range users{
                if i == len(users)-1 {
                        buffer.WriteString(fmt.Sprintf("('%s', '%d', '%s', '%s', '%s');", u.Birthday.Format("2006-01-02 15:04:05"), u.Age, u.Name, u.Email, u.Description))
                } else {
                        buffer.WriteString(fmt.Sprintf("('%s', '%d', '%s', '%s', '%s'),", u.Birthday.Format("2006-01-02 15:04:05"), u.Age, u.Name, u.Email, u.Description))
                }
        }

        return db.Exec(buffer.String()).Error
}

        fmt.Println("-------------Batch insert------------------")
        user1 := User{UserId: 1,
//      Birthday: time.Date(1987, 8, 15, 2, 15, 0, 0, time.Now().Location()).Format("1987-08-15 03:15:00"),
        Birthday: time.Date(1987, 8, 15, 2, 15, 0, 0, time.Now().Location()),
                Age: 32,
                Name: "wangq",
                Email: "" + strconv.Itoa(int((time.Now().Unix()))) + "88wangq@126.com",
                Description: "third",
        }
        numUsers := 10
        var users [10]User
        for i:=0; i < numUsers; i++ {
                users[i] = user1
                users[i].UserId = int64(numUsers + i)
                users[i].Email = "" + strconv.Itoa(i) + "88wangq@126.com" + strconv.Itoa(int((time.Now().Unix())))
        }
        err = BatchSave(GDB, users[:])
        if err != nil{
                fmt.Println("BatchSave error: ", err)
                return
        }
package main

import (
    "fmt"
    "os"
    "time"
    _"strconv"
    "database/sql"

    "github.com/jinzhu/gorm"

    "gormtest/models"
)

var (
    DBType = "mysql"
    DBUser = "root"
    DBPasswd = "123456"
    DBHost = "127.0.0.1"
    DBName = "wblog"
//    DBTablePrefix = "blog_"
    DBTablePrefix = ""
)
var GDB *gorm.DB

func CreateTable(db *gorm.DB, t interface{}){
    if db.HasTable(t){
        db.AutoMigrate(t)
    } else {
        db.CreateTable(t)
    }
}

type Auth struct {
    ID          int     `gorm:"primary_key" json:"id"`
    Username    string  `json:"username"`
    Password    string  `json:"password"`
}

func CheckAuth(db *gorm.DB, username, passwd string) bool{
    var auth Auth
    db.Select("id").Where(Auth{Username: username, Password: passwd}).First(&auth)
    if auth.ID > 0 {
        fmt.Printf("auth: %#v\n", auth)
        return true
    }

    return false
}

type User struct {
    gorm.Model
    UserId      int64            `gorm:"index"`
    Birthday    time.Time
    Age         int                `gorm:"column:age"`
    Name        string            `gorm:"size:255;index:idx_name_add_id"`
    Email       string            `gorm:"type:varchar(100);unique_index"`
    AddressID   sql.NullInt64    `gorm:"index:idx_name_add_id"`
    IgnoreMe    int                `gorm:"_"`
    Description string            `gorm:"size:2019;comment:'用户描述字段'"`
    Status      string            `gorm:"type:enum('published', 'pending', 'deleted');default:'pending'"`
    Auths        []Auth            `gorm:"foreignkey:ID;association_foreignkey:UserId"`
}

//设置表名,默认是结构体的名的复数形式
func (User) TableName() string {
    return "VIP_USER"
}


func main(){
    var err error
    GDB, err := models.NewDB(DBType, DBUser, DBPasswd, DBHost, DBName, DBTablePrefix)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    defer GDB.Close()

    // create table. Usually create tables with tools previously.
    CreateTable(GDB, &Auth{})
    err = GDB.Create(&Auth{ID: 1, Username: "test", Password: "test123456"}).Error
    if err != nil {
        fmt.Println("================================error: ", err)
    }
    CheckAuth(GDB, "test", "test123456")

    CreateTable(GDB, &User{})
/*
    // create 
    fmt.Println("-----------------------------------")
    user := User{UserId: 1,
        Birthday: time.Date(1987, 8, 15, 2, 15, 0, 0, time.Now().Location()),
        Age: 32,
        Name: "wangq",
        Email: "" + strconv.Itoa(int((time.Now().Unix()))) + "88wangq@126.com",
        Description: "third",
    }
    GDB.Create(&user)
    // db.NewRecord(&user) 用于检查主键是否为空
    if ! GDB.NewRecord(&user) { // create user ruturn false
        fmt.Println("写入成功")
    } else {
        fmt.Println("写入失败")
    }
*/
    // retrieve
    fmt.Println("-----------------------------------")
    var u1, u2, u3 User
    var uu1, uu2 []User
    var count int

    GDB.First(&u1)
    fmt.Println(u1)
    GDB.Last(&u2, 2)
    fmt.Println(u2)

    GDB.Where("name=?", "wang").First(&u3)
    fmt.Println(u3, 2)

    //GDB.Where("name=?", "wangq").Find(&uu1).First(&u4).Count(&count)
    GDB.Where("name=? AND id IN (?)", "wangq", []int64{18, 19, 20}).Find(&uu1).Count(&count)
    fmt.Println(uu1, count)
    // -1 cancel condition, including offset and limit
    GDB.Where("name=?", "wangq").Limit(5).Find(&uu2).Limit(-1).Count(&count)
    fmt.Println(uu2, count)

    // update
    fmt.Println("-----------------------------------")
    GDB.Model(&User{}).Where("name=? AND id=?", "wangq", 14).Update("age", 35)
    GDB.Model(&User{}).Where("name=? AND id=?", "wangq", 15).Update(User{Age:40})
    GDB.Debug().Model(&User{}).Where("name=? AND id=?", "wangq", 16).Update(map[string]interface{}{"age":35, "name":"wangwang"})

    // delete
    // 删除时主键必须有值,否则会删除所有记录
    // 如果model有DeleteAt字段,将自动获得软删除功能!
    //当调用Delete方法时,记录不会真正从数据库中被删除,只会将DeletedAt字段的值设置为当前时间
    fmt.Println("-----------------------------------")
    GDB.Debug().Where("id=?", 13).Delete(&User{})
    GDB.Debug().Delete(&User{}, "age=?", 40)
    // 彻底删除
    GDB.Debug().Unscoped().Delete(&User{}, "age=?", 40)

    // 关联 
    fmt.Println("-------------Association------------------")
//    var ua1 User
//    GDB.Preload("Auths").Where("email=?", "3@house.com").First(&ua1)
    var ua1 []User
    GDB.Preload("Auths").Where("age=?", 35).Find(&ua1)
    fmt.Printf("%v\n", ua1)

    var ua2 User
    GDB.Where("email=?", "2@house.com").First(&ua2)
// when lists, traverse list
//    var ua2 []User
//    GDB.Where("age=?", 35).Find(&ua2)
    GDB.Model(&ua2).Association("Auths").Find(&ua2.Auths)
    fmt.Printf("%v\n", ua2)

    var ua3 User
    GDB.Where("email=?", "1@house.com").First(&ua3)
// when lists, traverse list
//    var ua3 []User
//    GDB.Where("age=?", 35).Find(&ua3)
//    err = GDB.Model(&ua3).Related(&ua3.Auths).Find(&ua3.Auths).Error // 遍历了整个auth列表 //异常
    err = GDB.Model(&ua3).Related(&ua3.Auths).Error // select * from auth where user_id = 2; //2 is from ua3
    if err != nil {
        fmt.Println("================================error: ", err)
    }
    fmt.Printf("%v\n", ua3)

    // Raw
    fmt.Println("-------------Raw------------------")
    type Result struct{
        Age        int
        Name    string
        Email    string
    }
    var result Result
    GDB.Raw("select name, age, email from VIP_USER where address_id=?", 1).Scan(&result)
    fmt.Printf("%v\n", result)

    fmt.Println("-------------Raw Rows------------------")
    rows, err := GDB.Raw("select name, age, email from VIP_USER where address_id in (1, 2, 3, 4,5)").Rows()
    if err != nil {
        fmt.Println("db.Raw().Rows() error: ", err)
        return
    }
    defer rows.Close()
    for rows.Next() {
        GDB.ScanRows(rows, &result)
        fmt.Printf("%v\n", result)
    }
}
//models/models.go
package models

import (
    "fmt"

    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

func NewDB(dbType, user, password, host, dbname, tablePrefix string) (*gorm.DB, error){
    var err error

    db, err := gorm.Open(dbType, fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local",
        user, password, host, dbname))

    if err != nil {
        fmt.Println(err)
        return nil, err
    }

    gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string) string  {
        return tablePrefix + defaultTableName;
    }

    db.SingularTable(true)
    db.LogMode(true)
    db.DB().SetMaxIdleConns(10)
    db.DB().SetMaxOpenConns(100)
    return db, nil
}

4. 关联

外键表示一个表中的一个字段被另一个表中的一个字段引用。外键用来建立主表与从表的关联关系,为两个表的数据建立连接,约束两个表中数据的一致性和完整性,实现一些级联操作。MySQL会自动为所有表的主键进行索引,但是外键字段必须由用户进行明确的索引。

对于两个具有关联关系的表而言,相关联字段中主键所在的表就是主表(父表),外键所在的表就是从表(子表)。

主表删除某条记录时,从表中与之对应的记录也必须有相应的改变。一个表可以有一个或多个外键,外键可以为空值,若不为空值,则每一个外键的值必须等于主表中主键的某个值。

注意:引入外键之后,外键列只能插入参照列存在的值,参照列被参照的值不能被删除,这就保证了数据的参照完整性。

从表的外键关联的必须是主表的主键,且主键和外键的数据类型必须一致。

Belongs to的主体是profile,其属于另一个模型(User),外键在主体中指定,在主体中保存(foreignkey),关联到从体中某字段(associate_foreignkey)。

Has oneHas many的主体是user,其拥有其他模型(profile),外键在主体中指定,指定从体中某字段为外键(foreignkey),关联主体中某字段(associate_foreignkey)。

属于(belongs to)

Belongs to会与另一个模型建立一对一关系,因此声明的每一个模型实例都会“属于”另一个模型实例。

Foreign Key,若要定义属于关系,外键必须存在, 默认外键使用所有者的类型名称及其主键。

type User struct {
  gorm.Model
  Name string
}

// `Profile` 属于 `User`, 外键是`UserID` 
type Profile struct {
  gorm.Model
  UserID int   // 默认外键UserID
  User   User
  Name   string
}
type Profile struct {
  gorm.Model
  Name      string
  User      User `gorm:"foreignkey:UserRefer"` // 将 UserRefer 指定为外键
  UserRefer uint
}

对于一个 belongs to 关系,GORM 通常使用所有者的主键作为外键的值,上例中,外键的值是 User  ID

可以用 association_foreignkey 标签来更改它,例如:

type User struct {
  gorm.Model
  Refer string
  Name string
}

type Profile struct {
  gorm.Model
  Name      string
  User      User `gorm:"association_foreignkey:Refer"` // 将 Refer 作为关联外键
  UserRefer string
}

可以用Related查找belongs to关系:

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

Has one

在一个 has one 关联中,其也与另一个 model 建立了一对一关系,但它和一对一关系有不同的语义(及结果)。 Has one 表示:model 的每一个示例都包含或拥有另一个 model 的示例。

 has one 关系中,被拥有 model 必须存在一个外键字段,用于保存所属 model 的主键。

外键名通常使用 has one 拥有者 model 的类型加 主键 生成,对于上面的例子,其外键名为 UserID.

// User 只能有一张信用卡 (CreditCard), CreditCardID 是外键
type CreditCard struct {
  gorm.Model
  Number   string
  UserID   uint
// UserName string
}

type User struct {
  gorm.Model
  CreditCard   CreditCard
}
type User struct {
  gorm.Model
  CreditCard CreditCard `gorm:"foreignkey:UserName"`
}

在 has one 关系中,被拥有 model 会使用其外键,保存拥有者 model 的主键,您可以更改保存至另一个字段通过association_foreignkey

type CreditCard struct {
  gorm.Model
  Number string
  UID    string
}

type User struct {
  gorm.Model
  Name       `sql:"index"`
  CreditCard CreditCard `gorm:"foreignkey:uid;association_foreignkey:name"`
}

可以通过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

在一个 has many 关联中,其也与另一个 model 建立了一对多关系,不同于 has one,model 的拥有者可以有零个或多个实例。

// User 可以有多张信用卡(CreditCards), UserID 是外键
type User struct {
  gorm.Model
  CreditCards []CreditCard
}

type CreditCard struct {
  gorm.Model
  Number   string
  UserID  uint 
// UserRefer uint
}
type User struct {
  gorm.Model
  CreditCards []CreditCard `gorm:"foreignkey:UserRefer"`
}

Foreign Key,在 has many 关系中,被拥有 model 必须存在一个外键字段,默认的外键字段名称通常使用其拥有者 model 加上它的主键(比如 UserID, CardID, 等)。

要使用另一个字段作为外键,你可以通过标签 foreignkey 来定制它。

可通过association_foreignkey修改关联外键。

type User struct {
  gorm.Model
  MemberNumber string
  CreditCards  []CreditCard `gorm:"foreignkey:UserMemberNumber;association_foreignkey:MemberNumber"`
}

type CreditCard struct {
  gorm.Model
  Number           string
  UserMemberNumber string
}

可以通过Related使用has many关联:

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

预加载

Preload()方法的参数应该是主体结构的字段名。

// 下面的例子会用到 User 和 Order 结构体
type User struct {
  gorm.Model
  Username string
  Orders Order
}
type Order struct {
  gorm.Model
  UserID uint
  Price float64
}
// Preload 方法的参数应该是主结构体的字段名
db.Preload("Orders").Find(&users)
//// SELECT * FROM users;
//// SELECT * FROM orders WHERE user_id IN (1,2,3,4);
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

关联查询示例

定义了一个 User 和 Company, User 中可以包含多个 Company, 如下:

type User struct {
        ID        int        `gorm:"TYPE:int(11);NOT NULL;PRIMARY_KEY;INDEX"`
        Name      string     `gorm:"TYPE: VARCHAR(255); DEFAULT:'';INDEX"`
        Companies []Company  `gorm:"FOREIGNKEY:UserId;ASSOCIATION_FOREIGNKEY:ID"`
        CreatedAt time.Time  `gorm:"TYPE:DATETIME"`
        UpdatedAt time.Time  `gorm:"TYPE:DATETIME"`
        DeletedAt *time.Time `gorm:"TYPE:DATETIME;DEFAULT:NULL"`
}
type Company struct {
        gorm.Model
        Industry int    `gorm:"TYPE:INT(11);DEFAULT:0"`
        Name     string `gorm:"TYPE:VARCHAR(255);DEFAULT:'';INDEX"`
        Job      string `gorm:"TYPE:VARCHAR(255);DEFAULT:''"`
        UserId   int    `gorm:"TYPE:int(11);NOT NULL;INDEX"`
}

在查询 User 时希望把 Company 的信息也一并查询, 有以下三种方法:

Related

使用 Related 方法, 需要把 User 查询好, 然后根据 User 定义中指定的 FOREIGNKEY 去查找 Company, 如果没定义, 则调用时需要指定, 如下:

var u User
db.First(&u)
db.Model(&u).Related(&u.Companies)

User 列表时遍历列表一一查询 Company

Association

使用 Association 方法, 需要把 User 查询好, 然后根据 User 定义中指定的 AssociationForeignKey 去查找 Company, 必须定义, 如下:

var u User
db.First(&u)
db.Model(&u).Association("Companies").Find(&u.Companies)

Preload

使用 Preload 方法, 在查询 User 时先去获取 Company 的记录, 如下:

// 查询单条 user
var u User
db.Debug().Preload("Companies").First(&u)
// 对应的 sql 语句
// SELECT * FROM users LIMIT 1;
// SELECT * FROM companies WHERE user_id IN (1);

// 查询所有 user
var list []User
db.Debug().Preload("Companies").Find(&list)
// 对应的 sql 语句
// SELECT * FROM users;
// SELECT * FROM companies WHERE user_id IN (1,2,3...);

5. 钩子Hooks/callback

Hooks(一般称之为钩子函数)的功能是在运行创建/查询/更新/删除语句之前或者之后执行。

如果你为一个 model 定义了一个具体的方法,它将会在运行 创建,更新,查询,删除时自动被调用,并且如果任何回调函数函数返回一个错误,GORM 将会停止接下来的操作并且回滚当前的事务。

Scope包含当前对数据库操作的所有信息。

Scope contain current operation's information when you perform any operation on the database

type Scope struct {    
    Search  *search    
    Value   interface{}    
    SQL     string    
    SQLVars []interface{}
    // contains filtered or unexported fields
}

Gorm使用可链接的API,*gorm.DB是链的桥梁,对于每个链API,它将创建一个新的关系。

当我们开始执行任何操作时,GORM将基于当前的*gorm.DB创建一个新的*gorm.Scope实例。并且基于当前操作的类型,它将调用注册的creating, updating, querying, deleting或row_querying回调来运行操作。

GORM本身由Callbacks提供支持,因此您可以根据需要完全自定义GORM。

注册新callback

func updateCreated(scope *Scope) {
    if scope.HasColumn("Created") {
        scope.SetColumn("Created", NowFunc())
    }
}
db.Callback().Create().Register("update_created_at", updateCreated)// 注册Create进程的回调

删除现有callback

db.Callback().Create().Remove("gorm:create")// Create回调中删除`gorm:create`回调

替换现有的callback

db.Callback().Create().Replace("gorm:create", newCreateFunction)

// 使用新函数`newCreateFunction`替换回调`gorm:create`用于创建过程

注册callback顺序

db.Callback().Create().Before("gorm:create").Register("update_created_at", updateCreated)
db.Callback().Create().After("gorm:create").Register("update_created_at", updateCreated)
db.Callback().Query().After("gorm:query").Register("my_plugin:after_query", afterQuery)
db.Callback().Delete().After("gorm:delete").Register("my_plugin:after_delete", afterDelete)
db.Callback().Update().Before("gorm:update").Register("my_plugin:before_update", beforeUpdate)
db.Callback().Create().Before("gorm:create").After("gorm:before_create").Register("my_plugin:before_create", beforeCreate)

 

参考:

1. https://gorm.io/docs/conventions.html  英文文档 中文文档  官网

2. http://gorm.book.jasperxu.com/中文文档   https://learnku.com/docs/gorm/v2    https://www.kancloud.cn/sliver_horn/gorm

3. GORM 关联查询简书

4. golang之mysql操作-GORM

5.「连载十」定制 GORM Callbacks

6.  golang操作mysql使用总结  sql操作

7. 连接池 必要性及设置

8. MySQL外键约束(FOREIGN KEY)  C语言中文网

9. MySQL中的外键是什么、有什么作用   知乎

10. MySQL中外键的定义、作用、添加和删除

posted @ 2020-07-11 19:41  yuxi_o  阅读(2443)  评论(0编辑  收藏  举报