go语言:GORM

时间:2020-08-01
本文章向大家介绍go语言:GORM,主要包括go语言:GORM使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

7.GORM

  • 对象关系映射。

    数据表  -> 结构体
    数据行  -> 结构体实例
    字段   -> 结构体字段
    
  • 优点:提高开发效率,缺点牺牲执行性能,灵活性,弱化SQL能力。

  • gorm下载: 官网

go get -u github.com/jinzhu/gorm

7.1数据库的连接

// 引包
import (
	 _ "github.com/jinzhu/gorm/dialects/mysql"
	"github.com/jinzhu/gorm"
)
// 结构体创建
type UserInfo struct {
	Id uint
	Name string
	Gender string
	Hobby string
}
  • 连接数据库
// 连接数据库 这里指定编码格式,可解析时间类型,时间取当地时间
	db, err :=gorm.Open("mysql", "root:123@(ip:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local")
	if err != nil{
		panic(err)
	}
// 关闭连接
	defer db.Close()

7.2数据库迁移和最简单CURD

  • 创建表 自动迁移。让结构体和数据表形成对应关系
db.AutoMigrate(&UserInfo{})
  • 插入一条数据
/ 创建数据行
	user1 := UserInfo{1,"jk","man","篮球"}
	db.Create(&user1)
  • 查询第一条数据
var u UserInfo
db.First(&u)
fmt.Println(u)
  • 更新一条数据
var u UserInfo
// 查询到第一条数据,然后更新
db.First(&u)
db.Model(&u).Update("hobby","running")
  • 删除操作
db.Delete(&u)

7.3 GORM模型定义

  • GORM 内置一个gorm.Model结构体,gorm.Model是一个包含了ID,CreteAt,UpdateAt,DeleteAt四个字端的Golang结构体。
type Model struct {
  ID uint `gorm:"primary_key"`
  CreateAt time.Time
  UpdateAt time.Time
  DeleteAt *time.Time
}
  • 你可以自行将它嵌入到你自己模型中:
type User struct {
	gorm.Model
  Name string
}
  • 模型定义示例
type User struct{
	gorm.Model// 内嵌Model
	Name string
	Age sql.NullInt64//零值类型
	Birthday *time.Time
	Email string `gorm:"type:varchar(100);unique_index"`
	Role string `gorm:"size:255"`// 设置字端大小255
	MemberNumber string `gorm:"unique;not null"`// 设置会员号,唯一且不为空
	Num int `gorm:"AUTO_INCREMENT"`//设置num为自增类型
	Address string `gorm:"index:addr"`// 给address设置索引为addr
	IgnoreMe int `gorm:"-"`//忽略本字端
}
  • 自定义字段为主键
type Animal struct {
  AnimalID int64 `gorm:"primary_key"`
  Name string
}
  • 表的名字默认是结构体复数,你也可以自定义表的名字
type Animal struct {
	AnimalID int64 `gorm:"primary_key"`
	Name string
}

// 自定义表名字,因唯一指定表名,不会受方法影响
func (Animal) TableName() string{
	return "self_animal"
}
  • 禁用表的复数形式
db.SingularTable(true)
  • 强行指定表名
// 使User结构体创建名叫 mysql_self_name表
db.Table("mysql_self_name").CreateTable(&User{})
  • 通过自定义方法设计表名
gorm.DefaultTableNameHandle = func (db *gorm.DB, defaultTableName string) string{
  return "SMS_" + defaultTableName
}
// 给创建的默认表名,加前缀SMS
  • 自定义列的名称
type User struct {
  Age sql.NullInt64 `gorm:"column:user_age"`
}
// 会新增一个user_age字端。
  • 时间类型

    CreatedAt

db.Create(&user)
db.Model(&user).Update("CreatedAt", time.Now())

​ UpdatedAt

db.Save(&user)
db.Model(&user).Update("name","xjk")

​ DeletedAt

  • 调用删除记录,将会设置DeletedAt字端为当前时间,而不是直接将记录从数据库中删除。

7.4 GORM 的CURD

  • 首先定义一个模型
type uGroup struct {
	Id int64
	Name string `gorm:"defalut:'wang'"`
	Age int64
}

7.4.1创建记录

  • NewRecord
var u = type uGroup struct {
	Id int64
	Name string `gorm:"defalut:'wang'"`
	Age int64
}{Name:"laobing2", Age:77}
pk := db.NewRecord(&u)
fmt.Println(pk)// true
// NewRecord 查询当主键为空返回true

// 创建用户
db.Create(&u)
pk2 := db.NewRecord(u)
// 此时用户已创建,已经有主键
fmt.Println(pk2)// false

7.4.2 默认值

  • 通过tag定义字段的默认值。
type uGroup struct {
   Id int64
   Name string `gorm:"default:'ss'"`
   Age int64
}
  • 值得注意是,在创建记录生成SQL语句会排除没有值或值为零的字段。在将记录插入到数据库后,Gorm会从数据库加载到那些字端默认值。
var u = uGroup{Name:"",Age:99}
db.Create(&u)
  • 所有字段零值,如: 0 "" false 或者其他零值。都不会保存到数据库中,但会使用他们默认值。若果想要避免此情况,可以考虑使用指针.
  • 方式1:指针方式实现
type uGroup struct {
	Id int64
	Name *string `gorm:"default:'ss'"`
	Age int64
}
var u = uGroup{Name:new(string), Age:77}
db.Create(&u)
  • 方式2:Scanner/Valuer接口方式
type uGroup struct {
	Id int64
	Name sql.NullString `gorm:"default:'ss'"`
	Age int64
}
var u = uGroup{Name:sql.NullString{"", true}, Age:55}
db.Create(&u)

7.4.3普通查询

  • 一般查询
var u uGroup
// 根据主键查询
db.First(&u)
// 获取第一条记录
db.Take(&u)
// 根据主键查询最后一条记录
db.Last(&u)
// 查询所有记录
var us []uGroup
db.Find(&us)
// 查询主键id=3记录
var u uGroup
db.First(&u, 3)

7.4.4 Where查

  • 普通SQL查询
var u uGroup
// 查询name=ss 的第一条记录
db.Where("name=?","ss").First(&u)
// 查询 所有满足name=ss的记录
var us []uGroup
db.Where("name=?","ss").Find(&us)
// 查询 name <>
db.Where("name <> ?","ss").Find(&us)
// IN 查询
db.Where("name IN (?)", []string{"ss","jj"}).Find(&us)
// LIKE
db.Where("name LIKE ?","%ss%").Find(&us)
// AND
db.Where("name = ? AND age >= ?", "ss", "25").Find(&us)
// 时间Time
db.Where("updated_at > ?", lastWeek).Find(&us)
db.Where("created_at BETWEEN ? AND ?",last, yest).Find(&us)

7.4.5 struct & Map查询

  • Struct demo
var u uGroup
// 查询name=ss,age=20
db.Where(&uGroup{Name:"ss",Age:20}).First(&u)
  • Map查询
// Map查询
var us []uGroup
db.Where(map[string]interface{}{"name":"ss","age":77}).Find(&us)
// SELECT * FROM u_groups WHERE name = "ss" AND age = 77;
  • 主键的切片
db.Where([]int64{1,2,5}).Find(&us)
// SELECT * FROM u_groups WHERE id IN (1, 2, 5)
  • 当通过结构体进行查询时,GORM将会只通过非零值字段查询。这意味着如果你的字段值为0,"",false或者其他零值时,将不会被用于构建查询条件。
db.Where(&User{name:"ss",Age:0}).Find(&us)
  • 不过你可以通过上面提到2个方法。通过指针或是Scanner/Valuer

7.4.6Not条件查询

db.Not("name","ss").First(&u)
// SELECT * FROM u_groups WHERE name <> "ss" LIMIT 1

// Not In
db.Not("name", []string{"ss","1234"}).Find(&us)

// 主键不在切片中
db.Not([]int64{1,2,3}).First(&u)

// 查询第一个
db.Not([]int64{}).First(&u)
// Struct
db.Not(uGroup{Name:"xx"}).First(&u)

7.4.7 Or条件查询

db.Where("name=?","ss").Or("age=?","43").Find(&us)
//SELECT * FROM u_groups WHERE name = 'ss' OR age = 43;
// Struct
db.Where("name='jk'").Or(uGroup{Name:"ming"}).Find(&us)
// Map
db.Where("name='jk'").Or(map[string]interface{}{"name":"ming"}).Find(&us)
//SELECT * FROM u_groups WHERE name = 'ss' OR name = 'ming';

7.4.8内联条件

  • 作用与Where查询类似,当内联条件与多个立即执行方法一起使用时,内联条件不会传递给后面立即执行。
db.First(&u,5)//适用整形
db.First(&u,"id=?","string_primary_key")//适用菲整形主键
db.Find(&u,"name <> ? AND age > ?","jk", 20)
// struct
db.Find(&us,uGroup{Age:20})
// SELECT * FROM u_groups WHERE age=20;

// Map
db.Find(&u,map[string]interface{}{"age":20})
// SELECT * FROM u_groups WHERE age=20;

7.4.9额外查询选项

  • 为查询SQL添加额外的SQL操作
db.Set("gorm:query_option","FOR UPDATE").First(&u,5)
// SELECT * FROM u_groups WHERE id=5 FOR UPDATE;

7.4.10FirstOrInit

  • 获取匹配的第一条记录,否则根据给定的条件初始化一个新的对象(仅支持struct 和 map 条件)
// 当没有找到
db.FirstOrInit(&u, uGroup{Name:"not exist"})
fmt.Println(u)//返回结果:{0 not exist 0}
// 当找到了
db.Where(uGroup{Name: "ss"}).FirstOrInit(&u)
fmt.Println(u)//返回结果: {1 ss 77}

// 通过map获取
db.FirstOrInit(&u,map[string]interface{}{"name":"ss"})
  • Attrs
    • 如果记录未找到,将使用参数初始化struct
// 1.未找到:
db.Where(uGroup{Name:"not exist"}).Attrs(uGroup{Age:22}).FirstOrInit(&u)
fmt.Println(u)// {0 not exist 22}
// 当你查询用户名: not exist不存在时候会 默认返回User{Name: "not exist", Age: 20}

// 当然你也可以这么写
db.Where(uGroup{Name:"not exist"}).Attrs("age", 20).FirstOrInit(&u)

// 2.找到了
相同写法。
  • Assign
    • 无论是否查询到,都将参数赋值给struct
// 未找到
db.Where(uGroup{Name:"not exist"}).Assign(uGroup{Age:22}).FirstOrInit(&u)
fmt.Println(u)// {0 not exist 22}
// 找到
db.Where(uGroup{Name:"ss"}).Assign(uGroup{Age:77}).FirstOrInit(&u)
fmt.Println(u)// {1 ss 77}

7.4.11FirstOrCreate

  • 获取匹配的第一条记录,否则根据给定的条件创建一个新的记录(仅支持struct和map条件)
// 未找到
db.FirstOrCreate(&u, uGroup{Name:"not exist"})
fmt.Println(u)// 没有找到,直接创建了:{15 not exist 0}
// 找到
db.Where(uGroup{Name: "ss"}).FirstOrCreate(&u)
fmt.Println(u)//{1 ss 77}
  • Attrs
    • 如果记录未找到,将使用参数创建struct 和记录
db.Where(uGroup{Name:"Tom"}).Attrs(uGroup{Age:20}).FirstOrCreate(&u)
fmt.Println(u) //未找到会创建: {16 Tom 20},找到了会返回结果
  • Assign
    • 不管记录是否找到,豆浆参数赋值给struct并保存数据库
db.Where(uGroup{Name:"Lucy"}).Assign(uGroup{Age:30}).FirstOrCreate(&u)
fmt.Println(u) // 未找到创建:{17 Lucy 30}

db.Where(uGroup{Name:"Lucy"}).Assign(uGroup{Age:32}).FirstOrCreate(&u)// 找到了,更改用户信息,age改为32

7.5GORM高级查询

7.5.1子查询

  • 通过*gorm.expr的子查询
type Order struct {
	DeletedAt interface{}
	State string
	Amount int
}
var orders Order
	// SELECT * FROM "orders"  WHERE "orders"."deleted_at" IS NULL AND (amount > (SELECT AVG(amount) FROM "orders"  WHERE (state = 'paid')));
db.Where("amount > ?",db.Table("orders").Select("AVG(amount)").Where("state = ?","sk2").SubQuery()).Find(&orders)
	fmt.Println(rest)
fmt.Println(orders)

7.5.2选择字段

  • Select 指定你想从数据库中检索的字段,默认会选择全部字段
var userinfo []uGroup
// 方式1
db.Select("name,age").Find(&userinfo)
fmt.Println(userinfo)
//// SELECT name, age FROM users;
// 方式2
db.Select([]string{"name","age"}).Find(&userinfo)
fmt.Println(userinfo)
// 方式3
db.Table("users").Select("COALESCE(age,?)", 42).Rows()
//// SELECT COALESCE(age,'42') FROM users;

7.5.3排序

  • Order 指定从数据库中检索出记录顺序。设置第二个参数reorder为true ,可以覆盖前面定义的排序条件。
var users []uGroup
db.Order("age desc,name").Find(&users)
fmt.Println(users)
// SELECT * FROM u_group order by age desc,name

// 多字段排序
db.Order("age desc").Order("name").Find(&users)
fmt.Println(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)

7.5.4数量

  • limit 指定数据库检索的最大记录数。
var users []uGroup
db.Limit(2).Find(&users)
fmt.Println(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)

7.5.5偏移

  • Offset,指定开始返回记录前要跳过的记录数。
var users []uGroup
db.Limit(2).Offset(1).Find(&users)
fmt.Println(users)
// SELECT * FROM u_groups limit 3 offset 1;

7.5.6统计总数

var users []uGroup
var count int
db.Where("name = ?", "xiaoming2").Or("name = ?","xiaoming3").Find(&users).Count(&count)
fmt.Println(users,count)
// [{2 xiaoming2 13} {3 xiaoming3 23}] 2


db.Model(&uGroup{}).Where("name = ?","xiaoming2").Count(&count)
fmt.Println(count)
// SELECT count(*) FROM users WHERE name = "xiaoming2"

db.Table("u_groups").Count(&count)
// SELECT count(*) FROM u_groups;

db.Table("u_groups").Select("count(distinct(name))").Count(&count)
//// SELECT count( distinct(name) ) FROM u_groups;
  • 注意:Count 必须是链式查询的最后一个操作,因为它会覆盖前面SELECT,但如果里面使用count时就不会覆盖。

7.5.7Group & Having

  • 示例1:
type Result struct{
	State string
	Total int
}

rows, err := db.Table("orders").Select("state,sum(amount) as total").Group("state").Rows()
	for rows.Next() {
		r := &Result{}
		err = db.ScanRows(rows,r)
		fmt.Println(r)
		if err != nil{
			fmt.Println(err)
			break
		}
	}
  • 示例2:
var res []Result
db.Table("orders").Select("state,sum(amount) as total").Group("state").Scan(&res)
fmt.Println(res)
  • 示例三
// Having
rows, err := db.Table("orders").Select("state,sum(amount) as total").Group("state").Having("sum(amount) > ?",30).Rows()
	if err != nil{
		fmt.Println(err)
	}
	for rows.Next() {
		r := &Result{}
		err = db.ScanRows(rows,r)
		if err != nil {
			fmt.Println(err)
			break
		}
		fmt.Println(*r)
	}
  • 示例四:
var res []Result
db.Table("orders").Select("state,sum(amount) as total").Group("state").Having("sum(amount) > ?",30).Scan(&res)
fmt.Println(res)

7.5.8连接

  • 通过Join进行连接。
type uGroup struct {
	Id int64
	Name string `gorm:"default:'ss'"`
	Age int64
}
type Email struct {
	Id int64
	Email string
	UserId int64
}
type Result2 struct {
		Uname string
		Ename string
}

	rows, err := db.Table("u_groups").Select("u_groups.name as uname, emails.email as ename").Joins("left join emails on emails.id = u_groups.id").Rows()
	for rows.Next(){
		r := &Result2{}
		err = db.ScanRows(rows,r)
		if err != nil{
			fmt.Println(err)
			break
		}
		fmt.Println(*r)
	}
  • 示例2:
type Result2 struct {
		Uname string
		Ename string
	}
var results []Result2
db.Table("u_groups").Select("u_groups.name as uname, emails.email as ename").Joins("right join emails on emails.id = u_groups.id").Scan(&results)
fmt.Println(results)
  • 示例3:
var u uGroup
db.Joins("JOIN emails ON emails.id = u_groups.id AND emails.email = ?", "235102030@qq.com").Joins("JOIN credit_cards ON credit_cards.id=u_groups.id").Where("credit_cards.common=?","common1").Find(&u)
fmt.Println(u)

7.5.9Pluck

  • Pluck 查询model中的一个列作为切片,如果想要查询多个列可以收纳柜Scan
var ages []int64
var users []uGroup
db.Find(&users).Pluck("age",&ages)
fmt.Println(ages)
  • Model指定表Struct
var names []string
db.Model(&uGroup{}).Pluck("name",&names)
fmt.Println(names)
  • 查找多个列
db.Select("name,age").Find(&users)
fmt.Println(users)

7.5.10扫描

  • Scan扫描结果至一个struct
type Result struct {
		Name string
		Age int
	}
var result Result
db.Table("u_groups").Select("name,age").Where("name=?","xiaoming").Scan(&result)
fmt.Println(result)
  • 示例2
var results []Result
db.Table("u_groups").Select("name,age").Where("id > ?",0).Scan(&results)
fmt.Println(results)
  • 示例3:
//原生SQL
db.Raw("SELECT name,age from u_groups WHERE name=?","xiaoming").Scan(&results)
fmt.Println(results)

7.5.11 链式操作相关

  • 链式操作,Gorm实现了链式操作接口,所以你可以把代码写成这样。
var results []Result
	tx := db.Where("name = ?","xiaoming")
	// 添加更多条件
	judge:=true
	if judge {
		tx = tx.Where("age > ?",5)
	} else {
		tx = tx.Where("age = ?",30)
	}
	tx.Find(&results)
	fmt.Println(results)
  • 在调用立即执行方法前不能生成Query语句,借助这个特性你可以i创建一个函数处理一些普通逻辑。

7.5.12范围

  • Scopes,Scopes 是建立在链式操作的基础之上。基于它,你可以抽取一些通用逻辑,写出更多可重用的函数库。
func UGroupAge20(db *gorm.DB) *gorm.DB{
	return db.Where("age > ?", 20)
}

func NameeqXiaoming(db *gorm.DB) *gorm.DB{
	return db.Where("name = ?","xiaoming")
}
func UGroupIdeq1(db *gorm.DB) *gorm.DB{
	return db.Where("id = ?", 1)
}

func uGroupInId(ids []int) func(db *gorm.DB) *gorm.DB{
	return func(db *gorm.DB) *gorm.DB {
		return db.Scopes(UGroupAge20).Where("id IN (?)",ids)
	}
}
var us []uGroup

db.Scopes(UGroupAge20,NameeqXiaoming,UGroupIdeq1).Find(&us)
fmt.Println(us)
db.Scopes(uGroupInId([]int{1,2})).Find(&us)
fmt.Println(us)

7.5.13多个立即执行

  • 在GORM中使用多个立即执行方法时,后一个立即执行方法会服用前一个立即执行方法的条件(不包含内联条件)
var users []uGroup
var count int64
db.Where("name Like ?", "xiaoming%").Find(&users,"id IN (?)", []int{1,2,3}).Count(&count)
fmt.Println(users)
fmt.Println(count)

7.6 更新

7.6.1更新所有字段

  • 使用Save() 默认会更新所有字段,即使你没有去赋值。
var user uGroup
	db.First(&user)
	fmt.Println(user)
	user.Name = "jk"
	user.Age = 22
	db.Save(&user)

7.6.2 更新修改字段

  • 使用Update或者Updates更新指定字段
// 更新单个属性,如果它有变化
db.Model(&user).Update("name","hello")
// 根据条件更新单个属性
db.Model(&user).Where("age = ?",21).Update("name","jk")
// 使用map更新多个属性,只会更新其中有变化属性
db.Model(&user).Updates(map[string]interface{}{"name":"xujunkai","age":18})
// 使用struct 更新多个属性,只会更新其中变化且非零的字段
db.Model(&user).Updates(uGroup{Name:"xjk",Age: 20})
// struct更新时,GORM只会更新那些非零值的字段。
// 对于 "" 0 false 不会发生任何更新
db.Model(&user).Updates(uGroup{Name:"",Age: 0})

7.6.3 更新选定字段

  • 如果你想更新或忽略某些字段,你可以使用Select,Omit
// 下面我们在map中更新name,age.但是我们只选择了name.所以只更新name
db.Model(&user).Select("name").Updates(map[string]interface{}{"name":"xujunkai","age":19})


// Omit 表示要忽略字段
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name":"xiaoming","age":30})

7.6.4无Hooks更新

  • 上面更新操作会自动运行Model的。BeforeUpdate,AfterUpdate方法。更新UpdateAt时间戳。更新时保存其Associations.如果你想调用这些方法,你可以使用UPdateColume,UpdateColumns
db.First(&user)
db.Model(&user).UpdateColumn("name","hello")
// 更新多个属性。类似Updates
db.Model(&user).UpdateColums(User{Name:"hello",Age:18})

7.6.5批量更新

  • 批量更新时,Hook(钩子函数)。不会更新。
// map方式批量更新
db.Table("u_groups").Where("id IN (?)",[]int{1,5,11}).Updates(map[string]interface{}{"name":"xjk","age":33})

// struct 批量更新 只会更新非零字段。如果想更新所有字段。请使用map
db.Model(uGroup{}).Updates(uGroup{Name:"xiaoming",Age: 15})

// RowsAffected 获取更新记录
res := db.Model(uGroup{}).Updates(uGroup{Name: "xjk",Age: 40}).RowsAffected
fmt.Println(res)

7.6.6使用SQL表达式

  • 查询表中第一条数据,然后更新
ar user uGroup
db.First(&user)


db.Model(&user).Update("age",gorm.Expr("age*?+?",4,1))
// UPDATE `u_groups` SET `age` = age * 4 + 1, `updated_at` = '2020-08-01 13:10:20'  WHERE `users`.`id` = 1;

db.Model(&user).Updates(map[string]interface{}{"age":gorm.Expr("age - ?",500)})

db.Model(&user).Where("age > 10").UpdateColumn("age",gorm.Expr("age - ?",5))

7.6.7其他更新选项

// 为 update SQL添加其他SQL
db.Model(&user).Set("gorm:update_option","OPTION (OPTIMIZE FOR UNKNOWN)").Update("name","hello")

7.7删除

7.7.1删除记录

  • 警告 删除记录时,请确保主键字段有值。GORM会通过主键去删除记录,如果主键为空,GORM会删除该model所有记录.
var user uGroup
db.First(&user)
db.Delete(&user)

7.7.2批量删除

  • 删除全部匹配记录
db.Where("email LIKE ?","%xjk%").Delete(uGroup{})

7.7.3软删除

  • 如果一个model有,DeletedAt字段,他将自动获得软删除功能,当调用Delete方法时,记录不会真正的从数据库中被删除,只会DeletedAt字段值会被设置为当前时间
db.Delete(&user)

// 批量删除
db.Where("age = ?",20).Delete(&u_Group{})

// 查询会忽略被软删除的记录
db.Where("age = ?",84).Find(&user)

// 通过Unscoped方法可以查询被软删除的记录
var users []uGroup
db.Unscoped().Where("age=?",84).Find(&users)
fmt.Println(users)

7.7.4物理删除

db.Unscoped().Delete(&user)

$flag 上一页 下一页