- Created by 郭强, last modified on Oct 08, 2022
一、设计背景
大家都知道易用性和易维护性一直是goframe
一直努力建设的,也是goframe
有别其他框架和组件比较大的一点差异。goframe
没有采用其他 ORM
常见的 BelongsTo
, HasOne
, HasMany
, ManyToMany
这样的模型关联设计,这样的关联关系维护较繁琐,例如外键约束、额外的标签备注等,对开发者有一定的心智负担。因此 框架不倾向于通过向模型结构体中注入过多复杂的标签内容、关联属性或方法,并一如既往地尝试着简化设计,目标是使得模型关联查询尽可能得易于理解、使用便捷。因此在之前推出了ScanList
方案,建议大家在继续了解With
特性之前先了解一下 模型关联-动态关联-ScanList 。
经过一系列的项目实践,我们发现ScanList
虽然从运行时业务逻辑的角度来维护了模型关联关系,但是这种关联关系维护也不如期望的简便。因此,我们继续改进推出了可以通过模型简单维护关联关系的With
模型关联特性,当然,这种特性仍然致力于提升整体框架的易用性和维护性,可以把With
特性看做ScanList
与模型关联关系维护的一种结合和改进。
本特性需要感谢 aries 提供的宝贵建议。
With
特性从goframe v1.15.7
版本开始提供,目前属于实验性特性。
二、举个例子
我们先来一个简单的示例,便于大家更好理解With
特性,该示例来自于之前的ScanList
章节的相同示例,改进版。
1、数据结构
# 用户表 CREATE TABLE `user` ( id int(10) unsigned NOT NULL AUTO_INCREMENT, name varchar(45) NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; # 用户详情 CREATE TABLE `user_detail` ( uid int(10) unsigned NOT NULL AUTO_INCREMENT, address varchar(45) NOT NULL, PRIMARY KEY (uid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; # 用户学分 CREATE TABLE `user_scores` ( id int(10) unsigned NOT NULL AUTO_INCREMENT, uid int(10) unsigned NOT NULL, score int(10) unsigned NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2、数据结构
根据表定义,我们可以得知:
- 用户表与用户详情是
1:1
关系。 - 用户表与用户学分是
1:N
关系。 - 这里并没有演示
N:N
的关系,因为相比较于1:N
的查询只是多了一次关联、或者一次查询,最终处理方式和1:N
类似。
那么Golang
的模型可定义如下:
// 用户详情 type UserDetail struct { gmeta.Meta `orm:"table:user_detail"` Uid int `json:"uid"` Address string `json:"address"` } // 用户学分 type UserScores struct { gmeta.Meta `orm:"table:user_scores"` Id int `json:"id"` Uid int `json:"uid"` Score int `json:"score"` } // 用户信息 type User struct { gmeta.Meta `orm:"table:user"` Id int `json:"id"` Name string `json:"name"` UserDetail *UserDetail `orm:"with:uid=id"` UserScores []*UserScores `orm:"with:uid=id"` }
3、数据写入
为简化示例,我们这里创建5
条用户数据,采用事务操作方式写入:
- 用户信息,
id
为1-5
,name
为name_1
到name_5
。 - 同时创建
5
条用户详情数据,address
数据为address_1
到address_5
。 - 每个用户创建
5
条学分信息,学分为1-5
。
g.DB().Transaction(func(tx *gdb.TX) error { for i := 1; i <= 5; i++ { // User. user := User{ Name: fmt.Sprintf(`name_%d`, i), } lastInsertId, err := g.Model(user).Data(user).OmitEmpty().InsertAndGetId() if err != nil { return err } // Detail. userDetail := UserDetail{ Uid: int(lastInsertId), Address: fmt.Sprintf(`address_%d`, lastInsertId), } _, err = g.Model(userDetail).Data(userDetail).OmitEmpty().Insert() if err != nil { return err } // Scores. for j := 1; j <= 5; j++ { userScore := UserScores{ Uid: int(lastInsertId), Score: j, } _, err = g.Model(userScore).Data(userScore).OmitEmpty().Insert() if err != nil { return err } } } return nil })
执行成功后,数据库数据如下:
mysql> show tables; +----------------+ | Tables_in_test | +----------------+ | user | | user_detail | | user_score | +----------------+ 3 rows in set (0.01 sec) mysql> select * from `user`; +----+--------+ | id | name | +----+--------+ | 1 | name_1 | | 2 | name_2 | | 3 | name_3 | | 4 | name_4 | | 5 | name_5 | +----+--------+ 5 rows in set (0.01 sec) mysql> select * from `user_detail`; +-----+-----------+ | uid | address | +-----+-----------+ | 1 | address_1 | | 2 | address_2 | | 3 | address_3 | | 4 | address_4 | | 5 | address_5 | +-----+-----------+ 5 rows in set (0.00 sec) mysql> select * from `user_score`; +----+-----+-------+ | id | uid | score | +----+-----+-------+ | 1 | 1 | 1 | | 2 | 1 | 2 | | 3 | 1 | 3 | | 4 | 1 | 4 | | 5 | 1 | 5 | | 6 | 2 | 1 | | 7 | 2 | 2 | | 8 | 2 | 3 | | 9 | 2 | 4 | | 10 | 2 | 5 | | 11 | 3 | 1 | | 12 | 3 | 2 | | 13 | 3 | 3 | | 14 | 3 | 4 | | 15 | 3 | 5 | | 16 | 4 | 1 | | 17 | 4 | 2 | | 18 | 4 | 3 | | 19 | 4 | 4 | | 20 | 4 | 5 | | 21 | 5 | 1 | | 22 | 5 | 2 | | 23 | 5 | 3 | | 24 | 5 | 4 | | 25 | 5 | 5 | +----+-----+-------+ 25 rows in set (0.00 sec)
4、数据查询
新的With
特性下,数据查询相当简便,例如,我们查询一条数据:
var user *User g.Model(tableUser).WithAll().Where("id", 3).Scan(&user)
以上语句您将会查询到用户ID为3
的用户信息、用户详情以及用户学分信息,以上语句将会在数据库中自动执行以下SQL
语句:
2021-05-02 22:29:52.634 [DEBU] [ 2 ms] [default] SHOW FULL COLUMNS FROM `user` 2021-05-02 22:29:52.635 [DEBU] [ 1 ms] [default] SELECT * FROM `user` WHERE `id`=3 LIMIT 1 2021-05-02 22:29:52.636 [DEBU] [ 1 ms] [default] SHOW FULL COLUMNS FROM `user_detail` 2021-05-02 22:29:52.637 [DEBU] [ 1 ms] [default] SELECT `uid`,`address` FROM `user_detail` WHERE `uid`=3 LIMIT 1 2021-05-02 22:29:52.643 [DEBU] [ 6 ms] [default] SHOW FULL COLUMNS FROM `user_score` 2021-05-02 22:29:52.644 [DEBU] [ 0 ms] [default] SELECT `id`,`uid`,`score` FROM `user_score` WHERE `uid`=3
执行后,通过g.Dump(user)
打印的用户信息如下:
{ "id": 3, "name": "name_3", "UserDetail": { "uid": 3, "address": "address_3" }, "UserScores": [ { "id": 11, "uid": 3, "score": 1 }, { "id": 12, "uid": 3, "score": 2 }, { "id": 13, "uid": 3, "score": 3 }, { "id": 14, "uid": 3, "score": 4 }, { "id": 15, "uid": 3, "score": 5 } ] }
5、列表查询
我们来一个通过With
特性查询列表的示例:
var users []*User g.Model(users).With(UserDetail{}).Where("id>?", 3).Scan(&users)
执行后,通过g.Dump(users)
打印用户数据如下:
[ { "id": 4, "name": "name_4", "UserDetail": { "uid": 4, "address": "address_4" }, "UserScores": null }, { "id": 5, "name": "name_5", "UserDetail": { "uid": 5, "address": "address_5" }, "UserScores": null } ]
6、条件与排序
通过With
特性关联时可以指定关联的额外条件,以及在多数据结果下指定排序规则。例如:
type User struct { gmeta.Meta `orm:"table:user"` Id int `json:"id"` Name string `json:"name"` UserDetail *UserDetail `orm:"with:uid=id, where:uid > 3"` UserScores []*UserScores `orm:"with:uid=id, where:score>1 and score<5, order:score desc"` }
通过orm
标签中的where
子标签以及order
子标签指定额外关联条件体积排序规则。
三、详细说明
想必您一定对上面的某些使用比较好奇,比如gmeta
包、比如WithAll
方法、比如orm
标签中的with
语句、比如Model
方法给定struct
参数识别数据表名等等,那这就对啦,接下来,我们详细聊聊吧。
1、gmeta
包
我们可以看到在上面的结构体数据结构中都使用embed
方式嵌入了一个gmeta.Meta
结构体,例如:
type UserDetail struct { gmeta.Meta `orm:"table:user_detail"` Uid int `json:"uid"` Address string `json:"address"` }
其实在GoFrame
框架中有很多这种小组件包用以实现特定的便捷功能。gmeta
包的作用主要用于嵌入到用户自定义的结构体中,并且通过标签的形式给gmeta
包的结构体(例如这里的gmeta.Meta
)打上自定义的标签内容(列如这里的`orm:"table:user_detail"`
),并在运行时可以特定方法动态获取这些自定义的标签内容。详情请参考章节:元数据-gmeta
因此,这里嵌入gmeta.Meta
的目的是为了标记该结构体关联的数据表名称。
2、模型关联指定
在如下结构体中:
type User struct { gmeta.Meta `orm:"table:user"` Id int `json:"id"` Name string `json:"name"` UserDetail *UserDetail `orm:"with:uid=id"` UserScores []*UserScore `orm:"with:uid=id"` }
我们通过给指定的结构体属性绑定orm
标签,并在orm
标签中通过with
语句指定当前结构体(数据表)与目标结构体(数据表)的关联关系,with
语句的语法如下:
with:当前属性对应表关联字段=当前结构体对应数据表关联字段
并且字段名称忽略大小写以及特殊字符匹配,例如以下形式的关联关系都是能够自动识别的:
with:UID=ID with:Uid=Id with:U_ID=id
如果两个表的关联字段都是同一个名称,那么也可以直接写一个即可,例如:
with:uid
在本示例中,UserDetail
属性对应的数据表为user_detail
,UserScores
属性对应的数据表为user_score
,两者与当前User
结构体对应的表user
都是使用uid
进行关联,并且目标关联的user
表的对应字段为id
。
3、With/WithAll
1)基本介绍
默认情况下,即使我们的结构体属性中的orm
标签带有with
语句,ORM
组件并不会默认启用With
特性进行关联查询,而是需要依靠With/WithAll
方法启用该查询特性。
With
:指定启用关联查询的数据表,通过给定的属性对象指定。WithAll
:启用操作对象中所有带有with
语句的属性结构体关联查询。
在我们本示例中,使用的是WithAll
方法,因此自动启用了User
表中的所有属性的模型关联查询,只要属性结构体关联了数据表,并且orm
标签中带有with
语句,那么都将会自动查询数据并根据模型结构的关联关系进行数据绑定。假如我们只启用某部分关联查询,并不启用全部属性模型的关联查询,那么可以使用With
方法来指定。并且With
方法可以指定启用多个关联模型的自动查询,在本示例中的WithAll
就相当于:
var user *User g.Model(tableUser).With(UserDetail{}, UserScore{}).Where("id", 3).Scan(&user)
也可以这样:
var user *User g.Model(tableUser).With(User{}.UserDetail, User{}.UserScore).Where("id", 3).Scan(&user)
2)仅关联用户详情模型
假如我们只需要查询用户详情,并不需要查询用户学分,那么我们可以使用With
方法来启用指定对象对应数据表的关联查询,例如:
var user *User g.Model(tableUser).With(UserDetail{}).Where("id", 3).Scan(&user)
也可以这样:
var user *User g.Model(tableUser).With(User{}.UserDetail).Where("id", 3).Scan(&user)
执行后,通过g.Dump(user)
打印用户数据如下:
{ "id": 3, "name": "name_3", "UserDetail": { "uid": 3, "address": "address_3" }, "UserScores": null }
3)仅关联用户学分模型
我们也可以只关联查询用户学分信息,例如:
var user *User g.Model(tableUser).With(UserScore{}).Where("id", 3).Scan(&user)
也可以这样:
var user *User g.Model(tableUser).With(User{}.UserScore).Where("id", 3).Scan(&user)
执行后,通过g.Dump(user)
打印用户数据如下:
{ "id": 3, "name": "name_3", "UserDetail": null, "UserScores": [ { "id": 11, "uid": 3, "score": 1 }, { "id": 12, "uid": 3, "score": 2 }, { "id": 13, "uid": 3, "score": 3 }, { "id": 14, "uid": 3, "score": 4 }, { "id": 15, "uid": 3, "score": 5 } ] }
4)不关联任何模型查询
假如,我们不需要关联查询,那么更简单,例如:
var user *User g.Model(tableUser).Where("id", 3).Scan(&user)
执行后,通过g.Dump(user)
打印用户数据如下:
{ "id": 3, "name": "name_3", "UserDetail": null, "UserScores": null }
四、使用限制
1、字段查询与过滤
可以看到,在我们上面的示例中,并没有指定查询的字段,但是在打印的SQL
日志中可以看到查询语句不是简单的SELECT *
而是执行了具体的字段查询。在With
特性下,将会自动按照关联模型对象的属性进行查询,属性的名称将会与数据表的字段做自动映射,并且会自动过滤掉无法自动映射的字段查询。
所以,在With
特性下,我们无法做到仅查询属性中对应的某几个字段。如果需要实现仅查询并赋值某几个字段,建议您对model
数据结构按照业务场景进行裁剪,创建满足特定业务场景的数据结构,而不是使用一个数据结构满足不同的多个场景。
我们来一个示例更好说明。假如我们有一个实体对象数据结构Content
,一个常见的CMS
系统的内容模型如下,该模型与数据表字段一一对应:
type Content struct { Id uint `orm:"id,primary" json:"id"` // 自增ID Key string `orm:"key" json:"key"` // 唯一键名,用于程序硬编码,一般不常用 Type string `orm:"type" json:"type"` // 内容模型: topic, ask, article等,具体由程序定义 CategoryId uint `orm:"category_id" json:"category_id"` // 栏目ID UserId uint `orm:"user_id" json:"user_id"` // 用户ID Title string `orm:"title" json:"title"` // 标题 Content string `orm:"content" json:"content"` // 内容 Sort uint `orm:"sort" json:"sort"` // 排序,数值越低越靠前,默认为添加时的时间戳,可用于置顶 Brief string `orm:"brief" json:"brief"` // 摘要 Thumb string `orm:"thumb" json:"thumb"` // 缩略图 Tags string `orm:"tags" json:"tags"` // 标签名称列表,以JSON存储 Referer string `orm:"referer" json:"referer"` // 内容来源,例如github/gitee Status uint `orm:"status" json:"status"` // 状态 0: 正常, 1: 禁用 ReplyCount uint `orm:"reply_count" json:"reply_count"` // 回复数量 ViewCount uint `orm:"view_count" json:"view_count"` // 浏览数量 ZanCount uint `orm:"zan_count" json:"zan_count"` // 赞 CaiCount uint `orm:"cai_count" json:"cai_count"` // 踩 CreatedAt *gtime.Time `orm:"created_at" json:"created_at"` // 创建时间 UpdatedAt *gtime.Time `orm:"updated_at" json:"updated_at"` // 修改时间 }
内容的列表页又不需要展示这么详细的内容,特别是其中的Content
字段非常大,我们列表页只需要查询几个字段而已。那么我们可以单独定义一个用于列表的返回数据结构(字段裁剪),而不是直接使用数据表实体对象数据结构。例如:
type ContentListItem struct { Id uint `json:"id"` // 自增ID CategoryId uint `json:"category_id"` // 栏目ID UserId uint `json:"user_id"` // 用户ID Title string `json:"title"` // 标题 CreatedAt *gtime.Time `json:"created_at"` // 创建时间 UpdatedAt *gtime.Time `json:"updated_at"` // 修改时间 }
2、必须存在关联字段属性
由于With
特性是通过识别数据结构关联关系,并自动执行多条SQL查询来实现的,因此关联的字段也必须作为对象的属性便于关联字段值得自动获取。简单地讲,with
标签中的字段必须存在于关联对象的属性上。
五、递归关联
如果关联的模型属性也带有with
标签,那么将会递归执行关联查询。With
特性支持无限层级的递归关联。以下示例,仅供参考:
// 用户详情 type UserDetail struct { gmeta.Meta `orm:"table:user_detail"` Uid int `json:"uid"` Address string `json:"address"` } // 用户学分 - 必修课 type UserScoresRequired struct { gmeta.Meta `orm:"table:user_scores"` Id int `json:"id"` Uid int `json:"uid"` Score int `json:"score"` } // 用户学分 - 选修课 type UserScoresOptional struct { gmeta.Meta `orm:"table:user_scores"` Id int `json:"id"` Uid int `json:"uid"` Score int `json:"score"` } // 用户学分 type UserScores struct { gmeta.Meta `orm:"table:user_scores"` Id int `json:"id"` Uid int `json:"uid"` Required []UserScoresRequired `orm:"with:id, where:type=1"` Optional []UserScoresOptional `orm:"with:id, where:type=2"` } // 用户信息 type User struct { gmeta.Meta `orm:"table:user"` Id int `json:"id"` Name string `json:"name"` UserDetail *UserDetail `orm:"with:uid=id"` UserScores []*UserScores `orm:"with:uid=id"` }
六、模型示例
根据当前的数据表,这里给了更多的一些模型编写示例供大家参考。
1、关联模型嵌套
type UserDetail struct { gmeta.Meta `orm:"table:user_detail"` Uid int `json:"uid"` Address string `json:"address"` } type UserScores struct { gmeta.Meta `orm:"table:user_scores"` Id int `json:"id"` Uid int `json:"uid"` Score int `json:"score"` } type User struct { gmeta.Meta `orm:"table:user"` *UserDetail `orm:"with:uid=id"` Id int `json:"id"` Name string `json:"name"` UserScores []*UserScores `orm:"with:uid=id"` }
嵌套的模型也支持嵌套,只要是结构体嵌套的都支持自动数据赋值。例如:
type UserDetail struct { Uid int `json:"uid"` Address string `json:"address"` } type UserDetailEmbedded struct { UserDetail } type UserScores struct { Id int `json:"id"` Uid int `json:"uid"` Score int `json:"score"` } type User struct { *UserDetailEmbedded `orm:"with:uid=id"` Id int `json:"id"` Name string `json:"name"` UserScores []*UserScores `orm:"with:uid=id"` }
2、基础模型嵌套
type UserDetail struct { gmeta.Meta `orm:"table:user_detail"` Uid int `json:"uid"` Address string `json:"address"` } type UserScores struct { gmeta.Meta `orm:"table:user_scores"` Id int `json:"id"` Uid int `json:"uid"` Score int `json:"score"` } type UserEmbedded struct { Id int `json:"id"` Name string `json:"name"` } type User struct { gmeta.Meta `orm:"table:user"` UserEmbedded *UserDetail `orm:"with:uid=id"` UserScores []*UserScores `orm:"with:uid=id"` }
3、模型不带meta
信息
模型中的meta
结构重要的是指定数据表名称,当不存在meta
信息时,查询的数据表将会自动以结构体名称的CaseSnake
名称。例如,UserDetail
将会自动使用user_detail
数据表名称,UserScores
将会自动使用user_scores
数据表名称。
type UserDetail struct { Uid int `json:"uid"` Address string `json:"address"` } type UserScores struct { Id int `json:"id"` Uid int `json:"uid"` Score int `json:"score"` } type User struct { *UserDetail `orm:"with:uid=id"` Id int `json:"id"` Name string `json:"name"` UserScores []*UserScores `orm:"with:uid=id"` }
七、后续改进
- 目前
With
特性仅实现了查询操作,还不支持写入更新等操作。
- No labels
44 Comments
xx
这个
tableUser
是什么?xx
哎,是
User{}
或user
张
是否支持跨数据库使用,有没有其他处理方案。直接拼接上库名会将Meta作为字段查询 SELECT `Meta`,
郭强
暂不支持跨数据库,你可以在github上提个issue回头我考虑下。
xx
UserDetail
这样的结构体匿名字段,User
打印不出来UserDetail
里面包含的字段;因为很多时候想要把UserDetail
里面的字段放在User
里的最外层郭强
我没试过嵌套使用
with
,如果不行的话你可以提个issue
记录一下。xx
能够多字段关联吗?比如:
郭强
不支持,表与表之间只能使用一个字段关联。
xx
那如果有多个关联到就得拿发回的数据再做一次for循环筛选,能不能加个筛选条件的地方,比如一个表某个子段值等于关联的表某个字段值,或者被关联的表的某个字段值等于指定值才返回结果
郭强
你这个需求感觉可以使用
ScanList
:模型关联-动态关联-ScanListxx
不是的,只是通常的联表多个条件关联查询而已,ScanList不满足,ScanList的关联方式太简单了不够通用
ysxpark
郭强
是的,目前只有自定义的结构体才支持,不支持自动读取表与表关联关系,下个版本的
CLI
工具链会支持自动获取。willem
不这样做就要反复定义entity了,求支持
郭强
ysxpark willem 大家好,我看了下,最新版本的话这种关联是支持的,大家可以再试试,具体参考单例:https://github.com/gogf/gf/blob/32f33b9f8c08fc18385ea7ebc863e80c8d0e31cd/database/gdb/gdb_z_mysql_feature_with_test.go#L1052
黄昌淮
想问问大家, 像这样的
model
, 它会以ParentId
的值一直向上获取父级的信息, 但是ParentId
是有默认0
的,orm:"with:id=parent_id"
程序好像会一直以
id=0
一直向数据库进行查询, 请问, 有什么办法能让它ParentId==0
时终止查询之类的吗,或者是不是我这样的做法不对, 新手, 请多多赐教
郭强
你没有使用
With*
方法是不会执行关联查询的。另外你看看文档的With
方法介绍。黄昌淮
啊不好意思,可能我没讲清楚,我刚发现是我的别的地方出了一点错,修改后已经能正常使用了。
虽然怪不好意思的但还是另外有个地方想请教您: With 是不是只能查一层的数据的, 还是说是我的代码又又有问题亚:
(评论列表, 每个评论还有子评论列表, 子评论又有孙子评论列表。。。这样下去),
我这好像只能往下查一层:
{
"id": 1,
"content": "testtesttest",
"ParentId": 0,
"SubComments": [
{
"id": 5,
"content": "sdgsadg",
"ParentId": 1,
"SubComments": null # parent_id == 5的评论不能获取解析到这里。。。
},
{
"id": 9,
"content": "dasgsdag",
"ParentId": 1,
"SubComments": null
},。。。
]
}
这是我的model:
type CommentTest2 struct {
gmeta.Meta `orm:"table:comments"`
Id uint `orm:"id,primary" json:"id"`
Content string `orm:"content" json:"content"`
ParentId uint `orm:"parent_id"`
SubComments []*CommentTest2 `orm:"with:parent_id=id" json:"SubComments"` //子评论列表,条件:子评论的parentid==当前id
}
这是我查询数据库的代码
func (art *article) Test() []*model.CommentTest2 {
var CommentList []*model.CommentTest2
dao.Comments.WherePri(1).WithAll().Scan(&CommentList) //test拿id==1的评论以及它的子/孙评论门
return CommentList
}
请问我应该修改哪些
郭强
可以无限嵌套的,不过我只测试过一层的。如果有问题可以把可执行的代码和相关SQL提到github issue上,我运行试试。
tangdada
请问当我使用with模型关联的时候,比如您提交的例子,用户表,关联了用户学分,当我with用户学分模型的时候,这个学分模型如何加Where条件呢,比如查询用户列表,然后关联用户学分,其中关联的学分数据这一部分我只需要学分大于2的这种,请问一下这种情况该如何写啊
郭强
暂不支持静态关联模型还带额外的条件,你可以提个issue记录下后续改进。
hyh
WithAll()
查询时,如何对子表指定Order
?郭强
目前不支持,下个版本支持。
尘雨雾录
在字段过滤这一块,一定要写上关联的userid吗,因为最后列表中可能只需要标题,修改时间这两个内容字段就够了。而userid只是用来关联用的,可这样子最后查出来后,我返回给前端,是会带上userid的,但我作为后端自然是不想要他知道这个userid。
能否在结构体使用定义上,无需强加关联id,方便最后查询出来后直接返回给前端需要看到的字段
郭强
关联与返回的逻辑是独立的,是不同的组件控制的。你可以通过
`json:"-"`
的标签来设置不返回该字段。zhizhu
如何在关联的子表上使用sum、 count、 contact等方法?谢谢
郭强
目前不支持复杂的自动子查询,建议业务上分开自行查询,便于管理维护。
高照
请问目前有增加复合主键的计划吗
郭强
请提
issue
描述记录。beautiful
这样操作,造成死循环查询:
这里的
WithAll
造成嵌套with
,死循环执行查询。郭强
该
issue
反馈后不久已解决。米
郭强 with多个的话, 出现了概率性能查得到
查看sql日志 会发现关联的某些sql语句没有执行的情况
ciel
ScanList挺好用的, 可以直接使用 框架生成的 do。如下面的代码
但 with特性需要自己配置更多的关联关系,而且必须加
感觉不是很好用。
不过更期待大佬的 分表查询特性的出现。
chengqi
像上述model定义的话会造成死循环,如下
这种情况该如何处理呢?如果关联的是1对多 一个数组类型的就不会造成死循环情况,如下
郭强
Ray
我想问下 这些关联写在哪里呢 在 model 新建个文件 写吗 是不是不能写在 entiy 生成文件
郭强
icecream
递归关联到第三层之后 会重复执行多条sql
目前的表关系 主订单表关联-》[]*子订单表→*商品表
运行后 有多少个子订单就会查询多少次 商品表数据
hui
目前通过 tag 指定 where 查询条件的方式,如果参数是动态变化的貌似无法实现?比如 where: score>${score}
天道不公
我在dao生成的文件里面修改关联信息
目录:demo1/internal/model/entity/author.go
糖水不加糖
entity头部第一段就是 Code generated by GoFrame CLI tool. DO NOT EDIT. 把struct复制出来写到dao下对应的表文件中.
天道不公
求demo。。。。
糖水不加糖
假设有个user表 cli生成后对应有dao model model/do model/entity这四个地方都会有一个user.go文件 其中dao model下的两个就是可以定义的地方dao下定义方法 model下定义结构 如果不想太离散可以写到dao下 demo type AuthorWithBanks struct {....} 然后引用的时候 dao.AuthorWithBanks / modle.AuthorWithBanks
天道不公
嗯,解决了。兄弟,谢谢
天道不公
你好,请教一下如是使用中间表?
pei
n:n 可以给个例子看看吗?