gorm使用原生sql功能
存在嵌套情况下的预加载全部clause.Associations方法未实现问题。
目录
准备工作
建立数据库连接
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
var db *gorm.DB
func OpenDB() {
dsn := "root:adss123@tcp(127.0.0.1:3306)/go_db?charset=utf8mb4&parseTime=True&loc=Local"
res, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
db = res
if err != nil {
log.Fatal(err)
}
fmt.Printf("成功:%v\n", db)
}
一对多关系,HasMany
建表
has many 与另一个模型建立了一对多的连接。 不同于 has one,拥有者可以有零或多个关联模型。
例如,您的应用包含 user 和 credit card 模型,且每个 user 可以有多张 credit card。
// User 有多张 CreditCard,UserID 是外键
type Dogs struct {
gorm.Model
Name string
OwnersID int
}
type Owners struct {
gorm.Model
Name string
Dogs []Dogs
}
以此建表
type Dogs struct {
gorm.Model
Name string
OwnersID int
}
type Owners struct {
gorm.Model
Name string
Dogs []Dogs
}
func CreateHasMany() {
OpenDB()
db.AutoMigrate(&Owners{}, &Dogs{})
}
创建的表结构与一对一创建的表结构看上去没区别。
重写外键
要定义 has many 关系,同样必须存在外键。 默认的外键名是拥有者的类型名加上其主键字段名
例如,要定义一个属于 User 的模型,则其外键应该是 UserID。
此外,想要使用另一个字段作为外键,您可以使用 foreignKey 标签自定义它:
type User struct {
gorm.Model
CreditCards []CreditCard `gorm:"foreignKey:UserRefer"`
}
type CreditCard struct {
gorm.Model
Number string
UserRefer uint
}
创建记录
func InsertHasMany() {
OpenDB()
dog1 := &Dogs{Name: "狗子1号"}
dog2 := &Dogs{Name: "狗子2号"}
owners := &Owners{Name: "ylj", Dogs: []Dogs{*dog1, *dog2}}
db.Create(owners)
}
记录为
dogs
owners
简单发现,在嵌套情况下,创建最外层记录,会自动创建内层记录。
查询功能(预加载)
查询功能,与单表查询没太大区别,还是着重实验预加载功能。
未使用预加载功能:
func QueryHasMany() {
OpenDB()
owner := &Owners{}
db.Model(owner).First(owner, 1)
fmt.Println(owner)
}
未出现owner下属的dog信息
添加预加载功能:
func QueryHasMany() {
OpenDB()
owner := &Owners{}
db.Model(owner).Preload("Dogs").First(owner, 1)
fmt.Println(owner)
}
查询成功。
预加载全部
为方便观察与实验,在dog表上再关联一个dog_infos和dog_prices表
结构如下
type DogInfos struct {
gorm.Model
Age int
DogsID int
}
type DogPrices struct {
gorm.Model
Price int
DogsID int
}
type Dogs struct {
gorm.Model
Name string
OwnersID int
DogInfos DogInfos
}
type Owners struct {
gorm.Model
Name string
Dogs []Dogs
}
添加记录略古,直接展示dog_infos表记录
dog_prices
与创建、更新时使用 Select 类似,clause.Associations 也可以和 Preload 一起使用,它可以用来 预加载 全部关联,例如:
type User struct {
gorm.Model
Name string
CompanyID uint
Company Company
Role Role
Orders []Order
}
db.Preload(clause.Associations).Find(&users)
实验案例如下
func QueryHasMany() {
OpenDB()
dog := &Dogs{}
db.Preload(clause.Associations).First(dog)
fmt.Println(dog)
}
clause.Associations 不会预加载嵌套的关联,但你可以使用嵌套预加载 例如:
db.Preload("Orders.OrderItems.Product").Preload(clause.Associations).Find(&users)
实验案例如下
直接查询Owner
func QueryHasMany() {
OpenDB()
owner := &Owners{}
db.Preload(clause.Associations).First(owner)
fmt.Println(owner)
}
为解决问题。
我单纯的认为,在一次预加载的嵌套下,就可以加载出所有dog关联表。自认为的查询代码
db.Preload("Dogs").Preload(clause.Associations).First(owner)
但是结果并没有查询出info与price,问题待解决
带条件的预加载
GORM 允许带条件的 Preload 关联,类似于内联条件
// 带条件的预加载 Order
db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4) AND state NOT IN ('cancelled');
db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
// SELECT * FROM users WHERE state = 'active';
// SELECT * FROM orders WHERE user_id IN (1,2) AND state NOT IN ('cancelled');
实验案例如下
从dogs表下手查询
func QueryHasMany() {
OpenDB()
dog := &[]Dogs{}
db.Preload("DogInfos", "age < ?", "3").Find(dog)
fmt.Println(dog)
}
结果发现,dog1年龄4岁,并没有显示dog1的info表,dog2年龄2岁,显示了dog2的info表,限制条件生效。
从owner表下手
func QueryHasMany() {
OpenDB()
// dog := &[]Dogs{}
owners := &Owners{}
db.Preload("Dogs.DogInfos", "age < ?", "3").Find(owners, 1)
fmt.Println(owners)
}
约束条件依然有效
自定义预加载 SQL
您可以通过 func(db *gorm.DB) *gorm.DB 实现自定义预加载 SQL,例如:
db.Preload("Orders", func(db *gorm.DB) *gorm.DB {
return db.Order("orders.amount DESC")
}).Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4) order by orders.amount DESC;
实验案例如下
与上述的条件相同,只显示3岁以下的狗
直接从owner表下手
func QueryHasMany() {
OpenDB()
// dog := &[]Dogs{}
owners := &Owners{}
db.Preload("Dogs.DogInfos", func(db *gorm.DB) *gorm.DB {
return db.Where("age<?", 3)
}).Find(owners, 1)
fmt.Println(owners)
}
结果时成立的,约束起了效果
预加载信息作为条件子句经行CRUD
在上述的基础上,我只想查询出owner1的两条狗中哪儿条狗年龄小于3岁,另外一条不出现,则需要info表里的信息作为约束条件,来约束对owner下的dog查询。
实验案例如下
在条件函数下使用Preload方法。
func QueryHasMany() {
OpenDB()
// dog := &[]Dogs{}
owners := &Owners{}
db.Preload("Dogs", func(db *gorm.DB) *gorm.DB {
return db.Preload("DogInfos", "age<3")
}).Find(owners, 1)
fmt.Println(owners)
}
还是显示了dog1的信息,只是没显示dog1的年龄,没有起到预期效果
推测预加载功能,还是会价值出目标表的内容,约束条件只在本层Preload中起效果
改用Joins方法:
func QueryHasMany() {
OpenDB()
// dog := &[]Dogs{}
owners := &Owners{}
db.Preload("Dogs", func(db *gorm.DB) *gorm.DB {
return db.Joins("DogInfos").Where("age<3")
}).Find(owners, 1)
fmt.Println(owners)
}
Joins函数起到了预期效果
Preload 在一个单独查询中加载关联数据。而 Join Preload 会使用 inner join 加载关联数据