• gorm入门学习


    文档

    https://learnku.com/docs/gorm/v2/v2_release_note/9756
    https://gorm.cn/zh_CN/docs/
    

    下载安装

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

    简单示例

    package main
    
    import (
    	"fmt"
    	"time"
    	"gorm.io/driver/mysql"
    	"gorm.io/gorm"
    	"gorm.io/gorm/logger"
    )
    
    // 模型定义
    type Product struct {
    	ID       uint
    	Code     string
    	Price    uint
    	CreateAt time.Time
    	UpdateAt time.Time
    }
    
    func main(){
        // gorm数据库提前创建好
        dsn := "root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
        // 创建连接
        db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
            Logger: logger.Default.LogMode(logger.Silent),
        })
        if err != nil{
            fmt.Printf("conncetion failed, err:%v", err)
        }
        // 迁移创建表,默认创建到数据库的表名为结构体名+s
        db.AutoMigrate(&Product{})
    
    
        // 创建一条数据
        product := Product{
    		ID:       1,
    		Code:     "10086",
    		Price:    120,
    		CreateAt: time.Now(),
    		UpdateAt: time.Now(),
    	}
    	db.Create(&product)
        
        // 读
        var product Product
        db.First(&product, 1) // 根据整形主键查找
        db.First(&product, "code = ?", "D42") // 查找code字段值为D42
        
        // 更新单条
        db.Model(&product).Update("Price", 200) //将表products的price字段的值更新为200
        // 更新多个字段
        db.Model(&product).Updates(Product{Price:200, Code:"F42"})// 仅更新非零值字段
        // 使用map类型更新
        db.Model(&product).Updates(map[string]interfaces{}{
            "Price": 200,
            "Code": "F42"
        })
        
        
        // 删除
        db.Delete(&product, 1) // 根据主键删除
        
    }
    

    声明模型

    // 模型定义
    type User struct{
        ID	uint
        Name	string
        Email	*string
        Age	uint8
        Birthday	*time.Time
        MemberNumber	sql.NullString
        ActivedAt	sql.NullTime
        CreateAt	time.Time
        UpdateAt	time.Time
    }
    
    // gorm内部自己的结构体gorm.Model,可以直接嵌套到自己的结构体中
    type Model struct{
        ID	uint `gorm:"primaryKey"`
        CreateAt time.Time
        UpdateAt time.Time
        DeleteAt gorm.DeleteAt `gorm:"index"`
    }
    
    
    // 创建/更新时间
    type User struct {
      CreatedAt time.Time // 在创建时,如果该字段值为零值,则使用当前时间填充
      UpdatedAt int       // 在创建时该字段值为零值或者在更新时,使用当前时间戳秒数填充
    }
    
    
    // 正常结构体字段通过标签嵌入
    type Author struct{
        Name string
        Email string
    }
    type Blog struct{
        ID int
        Author Author `gorm:"embedded"
        Upvotes int32`
    }
    
    // 通过标签来为db中的字段名添加前缀
    type Blog struct{
      ID      int
      Author  Author `gorm:"embedded;embeddedPrefix:author_"`
      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
    }
    

    修改表名

    package main
    
    import (
    	"gorm.io/driver/mysql"
        "gorm.io/gorm"
    )
    
    type Student struct{ // 对应到 students这个表名
        Id int   // id
        Name String // name
        // Score float32 // score
        // tag方式
        Rank float32 `gorm:"column:score"` //score
        // UpdateAt time.Time // update_at
    }
    
    // 修改表名 默认表名是结构体名小写+s
    func (Student) TableName() string{
        return "student"
    }
    

    字段标签

    标签名 说明
    column 指定 db 列名
    type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSINED not NULL AUTO_INSTREMENT
    size 指定列大小,例如:size:256
    primaryKey 指定列为主键
    unique 指定列为唯一
    default 指定列的默认值
    precision 指定列的精度
    scale 指定列大小
    not null 指定列为 NOT NULL
    autoIncrement 指定列为自动增长
    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 无读权限
    - 忽略该字段,- 无读写权限

    mysql驱动程序的一些高级配置

    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
      DefaultStringSize: 256, // string 类型字段的默认长度
      DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
      DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
      DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
      SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
    }), &grom.Config{})
    

    gorm 增删改查

    gorm 增

    // 结构体定义
    type User struct{
        ID	uint
        Name	string
        Email	*string
        Age	uint8
        Birthday	*time.Time
        MemberNumber	sql.NullString
        ActivedAt	sql.NullTime
        CreateAt	time.Time
        UpdateAt	time.Time
    }
    
    // helper function
    func timePtr(t time.Time) *time.Time{
        return &t
    }
    
    // 创建一条数据
    // Birthday为指针类型,需要创建一个helper函数来返回time.Time的地址,不能直接使用&来直接取地址
    user := User{Name: "jinzhu", Age: 18, Birthday: timePtr(time.Now())}
    result := db.Create(&user)
    fmt.Println(user.ID)             // 返回数据的主键
    fmt.Println(result.RowsAffected) // 插入记录的条数
    
    // 选定字段创建
    user := User{Name: "liuwei", Age: 28, Birthday: timePtr(time.Now()), CreateAt: time.Now()}
    db.Select("Name", "Age", "CreateAt").Create(&user)
    
    // Omit排除选定字段
    user := User{Name: "jack", Age: 28, Birthday: timePtr(time.Now()), CreateAt: time.Now()}
    db.Omit("Age", "CreateAt").Create(&user)
    
    
    // 钩子,创建记录时会自动调用这些方法
    // BeforeSave/BeforeCreate, AfterSave/AfterCreate
    func (u *User) BeforeCreate(tx *gorm.DB)(err error){
        fmt.Println("创建之前调用...")
        return nil
    }
    user := User{Name: "JOJO", Age: 24, Birthday: timePtr(time.Now()), CreatedAt: time.Now()}
    db.Create(&user)
    
    
    // 批量插入
    var users = []User{{Name: "qiqi1"}, {Name: "qiqi2"}, {Name: "qiqi3"}}
    db.Create(&users)
    for _, user := range users {
        fmt.Println(user.ID)
    }
    
    // 使用map创建
    db.Model(&User{}).Create(map[string]interface{}{
        "Name":"wanglei",
        "Age": 25,
    })
    // 批量map插入
    db.Model(&User{}).Create([]map[string]interface{}{
        {"Name":"xiao1", "Age": 22},
        {"Name":"xiao2", "Age": 23},
        {"Name":"xiao3", "Age": 24},
    })
    
    
    // 关联创建(一对一),外键会自动关联
    db.Create(&User{
        Name: "liuwei",
        CreditCard: CreditCard{Number: "12345678"}
    })
    
    // 跳过关联创建数据
    user := User{Name:"wanglei", CreditCard: CreditCard{Number: "11111111"}}
    db.Omit("CreditCard").Create(&user)
    // 跳过所有关联clause.Associations
    db.Omit(clause.Associations).Create(&user)
    
    
    // uuid
    go get -u github.com/satori/go.uuid
    // 简单使用
    u, _ := uuid.NewV4()  //uuid.UUID类型,NewV4()每次返回不同uuid
    
    
    
    // 默认值使用标签default
    func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
    	u.UUID = uuid.NewV4().String()
    	return nil
    }
    
    type User struct {
    	ID   int64
    	Name string `gorm:"default: galeone"`
    	Age  int64  `gorm:"default:18"`
    	UUID string
    }
    func main(){
        // 默认会自动创建uuid
        db.Create(&User{Name: "liuwei1", Age: 28})
        db.Create(&User{Name: "liuwei2", Age: 29})
    }
    
    
    // 默认值零值问题
    // 当Age传入0时,存入到数据时会被识别为default,即使用default的值18
    db.Create(&User{Name: "liuwei3", Age: 0})
    
    // 零值问题解决
    // 将Age的类型int64改为指针类型*int64
    type User struct {
    	ID   int64
    	Name string `gorm:"default: galeone"`
    	Age  *int64  `gorm:"default:18"`
    	UUID string
    }
    
    

    gorm 查

    var user User
    
    //获取第一条记录(默认按主键升序)
    db.First(&user)
    fmt.Println(user.ID)
    fmt.Println(user.Name)
    
    // 获取第一条记录,没有指定排序字段
    db.Take(&user)
    fmt.Println(user.ID)
    fmt.Println(user.Name)
    
    // 获取最后一条记录
    db.Last(&user)
    
    //如果struct没有显示的定义主键,则按第一个字段排序
    type Language struct {
      Code string
      Name string
    }
    db.First(&Language{})
    
    
    // 主键检索
    // 只支持整形,字符串会导致sql注入问题
    // 查询id=10的数据
    db.First(&user, 10)
    // 查询id在1,2,3中的
    db.Find(&user, []int{1,2,3})
    
    
    // 检索对象
    // 获取全部数据,传入对象
    db.Find(&user)
    
    
    //获取第一条匹配的记录
    db.Where("name = ?", "jinzhu").First(&user)
    
    // 获取全部匹配记录
    db.Where("name <> ?", "jinzhu").Find(&user)
    
    // IN
    db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&user)
    
    // LIKE
    db.Where("name LIKE ?", "%jin%").Find(&user)
    
    //AND
    db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&user)
    
    // Time
    db.Where("updated_at > ?", lastWeek).Find(&user)
    
    // BETWEEN
    db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&user)
    
    
    // struct类型查询
    // 当使用结构作为条件查询时,GORM 只会查询非零值字段
    db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
    // Age不会作为查询条件
    db.Where(&User{Name: "jinzhu", Age: 0}).First(&user)
    
    
    // Map类型查询,可以使用零值作为查询条件
    db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&user)
    
    
    // 主键切片
    db.Where([]int64{20,21,22}).Find(&user)
    
    
    // 内联条件,用法与where相同
    db.Find(&user, "name = ?", "jinzhu")
    
    
    // Not条件
    db.Not("name  = ?", "jinzhu").First(&user)
    
    
    // Or条件
    db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&user)
    
    
    // 查询指定字段
    db.Select("name", "age").Find(&user)
    
    // COALESCE()函数返回传入的参数中第一个非null的值
    db.Table("users").Select("COALESCE(age,?)", 42).Rows()
    // SELECT COALESCE(age,'42') FROM users;
    
    
    // Order排序
    db.Order("age desc, name").Find(&user)
    // 指定多个字段排序
    db.Order("id desc").Order("age asc").Find(&users)
    
    
    //Limit限制返回的数量
    db.Limit(3).Find(&user)
    
    
    // Offset跳过指定的数量返回
    db.Offset(3).Find(&user)
    
    
    // Group分组 Having分组后过滤
    type result struct{
        Date time.Time
        Total int
    }
    db.Model(&User{}).Select("name,sum(age) as total").Where("name LIKE ?", "group%").Group("name").First(&result)
    // SELECT name, sum(age) as total FROM `users` WHERE name LIKE "group%" GROUP BY `name`
    
    db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
    // SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
    
    rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
    for rows.Next() {
      ...
    }
    
    rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
    for rows.Next() {
      ...
    }
    
    type Result struct {
      Date  time.Time
      Total int64
    }
    db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)
    
    
    
    // Distinct 去重
    db.Distinct("name", "age").Order("name, age desc").Find(&result)
    
    
    // Joins,左查询,右查询,内联查询
    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{})
    
    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)
    
    
    // Joins预加载,查询本表的同时将相关的外键表也同时查询出来
    db.Joins("Company").Find(&users)
    
    
    // Scan结果至struct,用法于Find类似
    type Result struct{
        Name string
        Age int
    }
    var result Result
    db.Table("users").Select("name", "age").Where("name = ?", "auto").Scan(&result)
    
    
    // 原生sql Raw
    db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)
    

    gorm 更新

    // Save保存
    type User struct{
        ID int64
        Name string
        Age int64
        Birthday time.Time
        UpdateAt time.Time
    }
    var user User
    db.First(&user)
    user.Name = "jinzhu666"
    user.Age = 100
    db.Save(&user)
    
    
    // 更新单个列,必须指定条件,且该对象主键有值
    // 当active=true时,更新name的值为hello
    db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
    
    
    // 更新多列
    // 根据struct更新,只会更新非零值的字段
    db.Model(&user).Updates(User{Name: "hello", Age: 13, Active: false})
    
    // 根据map更新属性
    db.Model(&user).Updates(map[string]interfaces{}{
        "name": "hello",
        "age": 18,
        "actived": false
    })
    
    // 更新钩子
    // 更新记录时会被自动调用
    func (u *User) BeforeUpdate(tx *gorm.DB)(err error){
        if u.Role == "admin"{
            return errors.New("admin user not allowed to update")
        }
        return
    }
    
    // 批量更新Updates
    db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
    
    
    // 使用sql表达式更新
    // product 的 ID 是 `3`
    DB.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))
    // UPDATE "products" SET "price" = price * 2 + 100, "updated_at" = '2013-11-17 21:34:10' WHERE "id" = 3;
    
    
    // 在更新时修改值
    若要在 Before 钩子中改变要更新的值,如果它是一个完整的更新,可以使用 Save;否则,应该使用 scope.SetColumn ,例如:
    func (user *User) BeforeSave(scope *gorm.Scope) (err error) {
      if pw, err := bcrypt.GenerateFromPassword(user.Password, 0); err == nil {
        scope.SetColumn("EncryptedPassword", pw)
      }
    }
    
    db.Model(&user).Update("Name", "jinzhu")
    

    gorm 删除

    // 删除一条记录,删除对象需要指定主键,否则会触发批量删除
    var email Email
    email.Id = 10
    db.Delete(&email)
    
    // 带条件删除
    db.Where("name = ?", "jinzhu").Delete(&email)
    
    // 根据主键删除
    db.Delete(&User{}, 10)
    var user User
    db.Delete(&user, []int{1,2,3})
    
    // 删除钩子
    func (u *User) BeforeDelete(tx *gorm.DB) (err error) {
        if u.Role == "admin" {
            return errors.New("admin user not allowed to delete")
        }
        return
    }
    
    // 批量删除,必须加条件
    db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
    db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
    

    创建表,一对一/一对多/多对多关联表

    // 一对一
    type CreditCard struct {
    	gorm.Model
    	Number string
    	UserID uint
    }
    
    type User struct {
    	gorm.Model
    	Name       string
    	CreditCard CreditCard
    }
    
    func main() {
    	dsn := "root:123456@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
    	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    	if err != nil {
    		fmt.Printf("connection failed, err:%v", err)
    	}
    	// 注意一下结构体的顺序
    	db.AutoMigrate(&User{}, &CreditCard{})
    }
    
    
    // 一对多 User 有多张 CreditCard,UserID 是外键
    type User struct {
      gorm.Model
      CreditCards []CreditCard // 切片
    }
    
    type CreditCard struct {
      gorm.Model
      Number string
      UserID uint
    }
    
    
    // 对多对
    // 会在两个 model 中添加一张连接表,使用AutoMigrate创建表时,会自动创建第三表
    // User 拥有并属于多种 language,`user_languages` 是连接表
    type User struct {
      gorm.Model
      Languages []Language `gorm:"many2many:user_languages;"`
    }
    
    type Language struct {
      gorm.Model
      Name string
    }
    
    
    // 多对多,自定义第三张连接表
    type Person struct {
      ID        int
      Name      string
      Addresses []Address `gorm:"many2many:person_addresses;"`
    }
    
    type Address struct {
      ID   uint
      Name string
    }
    
    type PersonAddress struct {
      PersonID  int
      AddressID int
      CreatedAt time.Time
      DeletedAt gorm.DeletedAt
    }
    
    func (PersonAddress) BeforeCreate(db *gorm.DB) error {
      // ...
    }
    
    // 修改 Person 的 Addresses 字段的连接表为 PersonAddress
    // PersonAddress 必须定义好所需的外键,否则会报错
    err := db.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})
    
    

    重写外键名

    //默认的外键使用拥有者的类型名加上主字段名
    //如下:其外键一般是 CompanyID
    type User struct {
      gorm.Model
      Name         string
      CompanyRefer int
      Company      Company `gorm:"foreignKey:CompanyRefer"`
      // 使用 CompanyRefer 作为外键
    }
    
    type Company struct {
      ID   int
      Name string
    }
    

    更改外键

    // 默认外键都是使用主键id作为外键
    type User struct {
      gorm.Model
      Name      string
      CompanyID string
      Company   Company `gorm:"references:Code"` // 使用 Code 作为外键
    }
    
    type Company struct {
      ID   int
      Code string
      Name string
    }
    

    外键约束

    // 你可以通过为标签 constraint 配置 OnUpdate、OnDelete 实现外键约束,在使用 GORM 进行迁移时它会被创建
    type User struct {
      gorm.Model
      Name      string
      CompanyID int
      Company   Company `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
    }
    
    type Company struct {
      ID   int
      Name string
    }
    

    反向引用(多对多)

    // User 拥有并属于多种 language,`user_languages` 是连接表
    type User struct {
      gorm.Model
      Languages []*Language `gorm:"many2many:user_languages;"`
    }
    
    type Language struct {
      gorm.Model
      Name string
      Users []*User `gorm:"many2many:user_languages;"`
    }
    

    关联标签

    标签 描述
    foreignKey 指定外键
    references 指定引用
    polymorphic 指定多态类型
    polymorphicValue 指定多态值、默认表名
    many2many 指定连接表表名
    joinForeignKey 指定连接表的外键
    joinReferences 指定连接表的引用外键
    constraint 关系约束,例如:OnUpdateOnDelete

    预加载

    // 查找一张表的时候,将另外一张表的数据也同时查询出来,用于优化查询
    type User struct {
      gorm.Model
      Username string
      Orders   []Order
    }
    
    type Order struct {
      gorm.Model
      UserID uint
      Price  float64
    }
    
    // 查找 user 时预加载相关 Order
    db.Preload("Orders").Find(&users)
    
    db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users)
    
    
    // Joins预加载,多用于一对一关系, inner join加载
    db.Joins("Company").Joins("Manager").Joins("Account").First(&user, 1)
    
    
    // 预加载全部关联clause.Associations
    type User struct {
      gorm.Model
      Name       string
      CompanyID  uint
      Company    Company
      Role       Role
    }
    db.Preload(clause.Associations).Find(&users)
    
    
  • 相关阅读:
    linux下查看机器是cpu是几核
    Stylus 安装使用图解
    npm 安装配置
    vue-cli vue脚手架
    nodejs与npm
    超详细解决 PLSQL下拉数据库"空白"
    Oracle 11g Windows64位
    Mysql 5.7.x zip windows安装
    Windows下Nginx的启动、停止、重启等命令
    Swagger中最常用的几个注解
  • 原文地址:https://www.cnblogs.com/weiweivip666/p/15941408.html
Copyright © 2020-2023  润新知