有两个 case,分别是不同人封装的,下面是伪代码:
case1:
// 根据用户昵称获取用户信息
function findUserByName(name){
// 获取数据库链接
db = _get_db()
if (not db){
return error
}
// orm 查询
user, err = db.where('name', name).get(table_name)
if (err) {
LOG("查找姓名失败", err)
return error
}
return user
}
// 根据用户 ID 获取用户信息
function findUserById(userid){
// 获取数据库链接
db = _get_db()
if (not db){
return error
}
// orm 查询
user, err = db.where('id', userid).get(table_name)
if (err) {
LOG("查找用户 id 失败", err)
return error
}
return user
}
// service 层调用
user1 = userRepo.findUserByName('拜拜你条尾')
user2 = userRepo.findUserById(12)
code ...
case2:
// 简单封装了查询函数?
function query(where_array, limit){
// 获取数据库链接
db = _get_db()
if (not db){
return error
}
// orm 查询
user, err = db.where(where_array).limit(limit).get(table_name)
if (err) {
LOG("查找用户失败", err)
return error
}
return user
}
// service 层调用
user1 = userRepo.query({name = '拜拜你条尾'}, 1)
user2 = userRepo.query({id = 12}, 1)
code ...
case2 这种封装有点迷的感觉,不能 cover 大部分的情况(where in/ or 之类),那这样的封装的意义何在?倒不如像第一种一样,不同的查询条件就封装一个方法,见名知意,不会给人带来疑惑?
不知道各位怎么看...
另外像请教下大家在这一层是怎么封装代码的?
1
waibunleung OP 太惨了....是没有人明白我在说什么吗....
|
2
juzzle 2021-05-28 14:35:11 +08:00
仓储层应该是组合数据吧,sql 语句你应该放到 model 层吧
|
3
gongym 2021-05-28 14:46:38 +08:00
这个应该都是生成的吧,自动生成 get 的方法和 query 方法。query 方法就是传入参数自动解析拼接成查询条件,这个就看封装能力了,一般的查询 like,eq,in 等还有排序条件等简单封装一下就够了吧。其余的情况就在生成代码的基础上自己写单独的方法实现
|
4
gongym 2021-05-28 14:55:54 +08:00
解析查询条件,简单点如果实体类和数据库字段对应规则可以确定的话,传入实体类范型,遍历字段和值,就可以完成 eq 条件查询了。如果不好对应就传入 map,key 是字段+查询情况( like,eq,ne,gt,lt 等) value 就是值。这样就完成了查询条件的拼接了
|
5
gongym 2021-05-28 14:57:39 +08:00
可以参考下 mybatis 生成的 example 和 criteria 。我感觉这个思路还是很强大的。就是太复杂了。我们用不到那么复杂后期就换 mybatis plus 了
|
6
simonlu9 2021-05-28 14:59:06 +08:00 1
参考下 jpa 吧,方便得一批
|
7
waibunleung OP |
8
waibunleung OP @gongym case2 不是自动生成的,是人写的
|
9
ksc010 2021-05-28 15:58:13 +08:00
case2 更灵活一些 我的理解 目的就是为了减少代码量
|
10
InvincibleDream 2021-05-28 16:06:45 +08:00
case2 改个名呗 query_with_limits 设置为私有,然后再写俩个 wrapper 一个 findUserByName 一个 findUserById 。这样两个方法的实现比较好维护。
|
11
meshell 2021-05-28 16:08:38 +08:00
工资按代码量选 1 反之 2//doge
|
12
BeautifulSoap 2021-05-28 16:09:12 +08:00
让我选,那应该是 case1 吧,case2 的想法其实是可以的,但写法有问题
case2 这写法泄露了数据结构。`user1 = userRepo.query({name = '拜拜你条尾'}, 1)` 中的 `name` 是具体 BD 的字段名 ,repository 目的之一不就是隔离具体的数据存储过程吗。这样写的话,意味着具体数据的结构溢出到了其他的领域层(比如调用 repo 的是 Service 层) case2 改改的话 `query(id int, name string, limit int)` 自动根据 id 和 name 哪个是空值自动创建 sql,或者动态语言的话直接 定义`query(user_info)` ,根据 user_info 的的类型( int 或 string )来创建对应的 sql (虽说感觉后者的方法可能今后不太好维护) |
13
devld 2021-05-28 16:11:08 +08:00 via Android
我理解的是,repository 层就不要向上层暴露细节了(比如 where 条件)。
#10 的就挺好的 |
14
thtznet 2021-05-28 16:24:48 +08:00
用 EF, 注入 context 。啊? java ?算了算了,手写吧。
|
15
waibunleung OP @ksc010 灵活只是相对的,case2 的做法只是能让人方便添加 where and 的条件,但是要兼容 where in 或者 where or 的时候要怎么办呢?而且取了个叫 query()的名字,看上去就比较兼容多种查询情况的样子....实际上又不是能做大部分的工作。
如果我有一个 where in 的查询,那我是不是要重新封装一个函数?那这个时候这个函数叫什么好? |
16
waibunleung OP @thtznet 动态语言
|
17
xiaoyiqingz 2021-05-28 16:40:42 +08:00
Repository 感觉应该隐藏掉数据的存储方式吧,比如数据会存储在不同的位置,不同类型数据库,甚至是在文件里,对外只提供获取数据的接口。也有可能需要获取的数据并不对应一个数据表,可以在 Repository 做一些处理,所以 findUserByName 这种表意且隐藏数据存储方式的感觉会更好一些。userRepo.query 这种方式感觉就是要操作数据库了
|
18
mcfog 2021-05-28 16:47:45 +08:00 via Android 1
1 是 repository 2 是 database helper
做成 1 调用 2 都行 |
19
waibunleung OP @mcfog 有 orm 的链式调用的,如果说要做成 1 再调用 2,那为什么不直接用 orm db.where(‘name’, name).get()
而是 function getUserByName(name){ return self.query({‘name’ = name}) } |
20
waibunleung OP @devld 有 orm 的链式调用的,如果说要做成 1 再调用 2,那为什么不直接用 orm db.where(‘name’, name).get()
而是 function getUserByName(name){ return self.query({‘name’ = name}) } |
21
ikesnowy 2021-05-28 18:32:49 +08:00
可以参考 CQRS 的操作。
由于查询不存在副作用,因此可以不用拘泥于 domain 和 repository,直接从 domain 的上一层调用 orm 查询就可以了,domain 只提供一个实体的数据结构(当然你也可以另外弄 DTO,直接去掉对 domain 的依赖,怎么方便怎么来) 于是 repository 就只需要一个 `getEntityById()` 的方法,由上层(调用方,或者 application service )先通过复杂查询搞到 Id,在执行写命令的时候提供这个 Id 就可以。 |
22
forgottencoast 2021-05-28 23:09:28 +08:00
简单,case 1 调用 case 2.
可以考虑把 case 2 作为高级接口开放出去。 |
23
mcfog 2021-05-29 01:08:25 +08:00 via Android
@waibunleung 是可以,不是必须或推荐
我说 2 是 helper 其实是说 2 不可能把自己叫 repo,理由其实和其他人一样的,封装层次的问题 至于 1 调 2 合理的可能性,比如 ORM 不够甜增加一些甜度,比如 where 是焦点问题(取决于项目情况可能有注入、扫表性能等等等各种痛点),通过收口( repo 不写 where 了)来解决某个问题 你也说了是跨语言聊,所以其实也说不了不同语言的常见 ORM 问题有啥,所以其实说不了 2 的存在合不合理,但 2 当作 repo 层的可能性是不存在的 |
24
waibunleung OP @forgottencoast 高级接口?有 orm 了这种高级接口就显得,你想用又不想用的样子....
|
25
waibunleung OP @mcfog 大概了解了
|
26
waibunleung OP @mcfog 但是我又有另一个疑问是,多条件搜索的时候,case1 这种写法应该怎么写?比如用户管理后台,可以根据用户 id,姓名,状态,类型等进行搜索那就要写个 getUserList(id, name, status, type, page, page_size){ code... } 这样的函数?
|
27
waibunleung OP 但是我又有另一个疑问是,多条件搜索的时候,case1 这种写法应该怎么写?比如用户管理后台,可以根据用户 id,姓名,状态,类型等进行搜索那就要写个 getUserList(id, name, status, type, page, page_size){ code... } 这样的函数?
@devld @BeautifulSoap @xiaoyiqingz @simonlu9 |
28
waibunleung OP 其实是再想问下动态查询的时候,repository 层的代码要怎么写比较好
|
29
BeautifulSoap 2021-05-29 23:20:07 +08:00
@waibunleung
搜索有一点儿复杂的时候,可以定义个搜索条件的 struct 或类,根据需要的搜索的内容填上信息。这个 struct/类是和具体表结构无关的。或者写个简单的 query builder,这个 qury builder 依旧是和具体数据结构无关,然后把 builder 交给 repository 进行具体的搜索 如果是非常复杂的搜索的话,应该使用 CQRS |
30
waibunleung OP @BeautifulSoap 构建 query builder 是在 service 层构建吧?那构建完了我在 service 层直接 execute 这个 builder 不就可以了吗,还要扔到 repository 层?如果扔到 repository 层,那 repository 层只是简单执行并返回结果就好了,感觉有点怪怪的....
我感觉是不是应该是 ``` // UserController 层 function List(req request) { id = req.id status = req.status name = req.name type = req.type page = req.page pageSize = req.page_size res = userService.getUserList(id, status, name, type, page, pageSize) return res } // UserService 层 function getUserList(id, status, name, type, page, pageSize) { res = userRepository.getUserList(id, status, name, type, page, pageSize) return res } // repository 层 function getUserList(id, status, name, type, page, pageSize) { db = getDb() // orm 对象 if (id > 0){ db.where('id', id) } if (status > 0){ db.where('status', status) } if (name != ''){ db.like('name', '%' + name + '%') } ....... return db.orderBy(id, 'desc).limit(page, page_size).get() } ``` |
31
BeautifulSoap 2021-05-30 11:58:00 +08:00
@waibunleung 你给 builder 定义个接口,然后 builder 的实现放到 infrastructure 里,上层调用 builder 的接口也没问题啊。我上面说把 builder 扔给 repository 是针对定义个简单搜索条件的 struct/类来说的,看做 struct/类的简单升级版。如果真想写个复杂点的 query builder,直接在 infrastructure 实现 query builder 会更好。如果还要复杂了,那考虑使用 CQRS
|
32
chaleaochexist 269 天前
大佬我遇到了和你一样的问题, 大佬最近有什么新体会吗?
欢迎分享. |