DAO 层和 ORM,能区分,但又不完全能区分,我裂开了

2021-05-06 14:22:54 +08:00
 waibunleung

desc:是这样的,小弟从 php 转到 go 一个月时间不够,项目的目录结构需要自己搭建。于是就找网上的项目组织目录参考,发现有一部分目录结构里有 dao 层的分层概念。这个在 java 里面比较常见,在 php 中用得比较多的是 orm,不知道 go 中是不是也适合这样搞。

于是引起了我一顿梳理 dao 和 orm 之间的关系。但仍有如下疑问:

  1. 究竟项目使用了 orm 之后,还需不需要再分一层 DAO 层出来?如果要分,此时的 DAO 层里面写的是什么逻辑?求 demo 举例
  2. ORM 不是正好替代了 DAO 的角色吗?还是说 DAO 可以通过 ORM 来实现?此时 DAO 层的实现是怎么样的?在 ORM 外面包一层,意义在哪里?
  3. 在项目代码分层上(语言无关),DAO 和 ORM 分别是什么层面上的东西?

真的纠结了三四天了,每天看完都有不同的结论,求大佬们指点一下你们在项目中是怎么划分的~

7585 次点击
所在节点    程序员
78 条回复
bsg1992
2021-05-07 22:17:14 +08:00
ORM 和 DAO 是不冲突的。Repository 和 DAO 不是一个概念 。
DAO 是单纯的对数据库进行增删改查的一些操作和特殊的业务逻辑。
一个好的 ORM 基本就代替了 DAO 层 90%的作用。可以看看 DotNet EntityFramework Core ORM 基本没有比他还好用的
如果你的项目涉及到多种数据源需要提供给 Service 这里可以引入 Repository 的概念
使用了 ORM 也没有必要在对 ORM 进行一层封装,这种封装我认为是脱裤子放屁。
waibunleung
2021-05-07 22:28:36 +08:00
@bsg1992 最后一点我可能不太认同,不想 orm 在 service 层满屏飞,还是在 dao 封装一层好一点,即使是简单的调用
meshell
2021-05-08 10:18:02 +08:00
@waibunleung symfony 的 容器注入实际就是反射构造函数的参数,然后注入的。laravel 了是一样的
bsg1992
2021-05-08 10:33:27 +08:00
@waibunleung 这样的封装 会导致失去 ORM 很多的特性,而且会导致 DAO 层越来越臃肿 出现很多 FindByXX 之类的 func
waibunleung
2021-05-08 13:55:05 +08:00
@bsg1992 那想请教下,你认为 orm 和 dao 共存的时候,具体的使用场景是怎么样的?什么时候直接用 orm 进行链式调用,什么时候用 dao 呢?可不可以举个例子区分一下呢?十分感谢!
VeeSong
2021-05-12 09:04:58 +08:00
dao 层获取一个对象(或一堆数据),至于这个对象怎么获得的,用 MySQL 、MongoDB 、Redis 、ES 还是文本文件,调用 dao 的人并不关心。如果用 MySQL 就用 MySQL 的 orm (如 Java 的 hibernate,PHP 的 fluent ),如果用 Redis 就用 Redis 的 orm 。
以后你想重构为其他任何的数据层 orm,调用 dao 的人都不需要做任何改动。
VeeSong
2021-05-12 09:27:15 +08:00
在 Java 中,dao 一般是先定义接口类,然后写对应的实现类。实现类可以是任何 orm,然后通过 spring 注入你想要的 dao 实现类。无论之后别人想换任何奇奇怪怪的 orm 都没问题,只要照着接口类去实现,然后用 Junit 跑一遍测试,测试 OK 的话修改下 spring 的注入配置就行了。
dao 用接口的形式封装,对测试和重构都非常友好
waibunleung
2021-05-13 16:30:59 +08:00
@VeeSong 明白,这是我期待的答案
decimalbell
2021-05-14 17:16:54 +08:00
考虑这个场景,查询用户信息,先从 Redis 缓存里面查,查不到再从数据库里面查
一般有如下文件:
user_dao.go
user_redis.go
user_db.go
你一直纠结的 ORM 只是在 db 这一层,dao 其实是更大的概念
waibunleung
2021-05-27 17:29:32 +08:00
@decimalbell 这三个文件里面写的是怎样的代码呢?可以举一个更详细的例子吗?
chaleaoch
2021-09-09 14:19:33 +08:00
@kop1989 大佬请教.
举例:比如我需要输出表 A 的行数,如果是单纯的 ORM,那我就只能直接获取本表的所有内容,返回一个 List<A>,然后我在业务层再去获取 List<A>的 size 。这明显是效率非常低下的。于是我就可以通过 DAO 层单独编写封装 SQL 去获取表 A 的行数,返回。
==========
如果 ORM 提供了获取 A 的行数,是否还是需要在 DAO 里面封一个方法,然后返回一个 int.
也就是说这个方法里面只有一条语句?


谢谢.
kop1989
2021-09-09 14:29:28 +08:00
@chaleaoch #71

假设你的程序架构设计沿袭的是传统分层模式。

如果你有 DAO 层,那么理论上需要。
换句话说,DAO 层理应负责的是所有的数据访问。不管你是一行语句实现,还是 100 行语句实现。
也就是说,上层业务代码通过,且必须通过 DAO 层来访问数据,这样才能保证你的 DAO 层设计是有意义的。

因为在传统的分层模式中,往往 DAO 层是由大数据开发、业务开发、甚至是 DBA 共同参与的。他们能够保证 DAO 层的高效、严谨、稳定。
chaleaoch
2021-09-09 15:17:49 +08:00
@FreeEx 拜读了大佬的项目,学到很多.
我的问题是, credential 部分无论是 repository 还是 service 中都看到了业务逻辑.
但是看楼上的评论. 感觉 DAO(repository) 里面不应该出现业务.

想听听你的见解.


另外还有一个小问题是: repository 是哪个项目里流出来的概念? 哪个插件 /组件 /库? 我从来都没听说过这个东西.

谢谢
chaleaoch
2021-09-09 16:28:34 +08:00
@kop1989 谢谢大佬回复.

问题: User Group 两张表. 但是在数据库和 ORM 层面他们没有外键关联关系.

现在的需求是我要他们的连表之后的数据(当然是用 Java/go/balabal).

这个代码应该放在 Service 里面写还是 DAO 里面写.

是不是 DAO 里面 是不能有任何业务.

那 DAO 里面的方法 100 行 都写些什么呢? 如果没有业务的话 没啥可写的啊.
FreeEx
2021-09-09 16:51:44 +08:00
@chaleaoch 纯粹的设计中 repository 是不应该出现业务逻辑的,只负责 DB 操作。之所以我把业务逻辑写到了 repository 里面是因为我在 golang 的开源库里没有找到合适的依赖注入库,初始化一个 service 挺麻烦的,我就为了省事把业务逻辑写到了 repository 里面,想着后期再优化修改,实际上这是不可取的(到现在我还没改)。

我了解 repository 模式在 SpringDataJpa 上面,repository 与 DAO ( Data Access Object )有着明显区别,repository 是面向对象的,一个 repository 只操作一种对象,对应到数据库也只是一个库,多表操作就需要封装到 service 里面,属于业务逻辑了,感觉有点像 DDD ; DAO 是面向数据的,经常会写一些多表的 sql,关于 db 的业务逻辑也包含在了 dao 里面。

PS:关于 golang 的开发模式我也在摸索中,代码不是很规范,希望你能取其精华,去其糟粕。
kop1989
2021-09-09 17:11:17 +08:00
@chaleaoch #74
“DAO 中不能掺杂任何业务逻辑”,主要是为了让 DAO 方法本身能够最大化的复用。
但到底什么是“业务”,什么是“数据库操作”其实还是由自己定义的。

我个人的习惯是,DAO 层只负责通过数据库的方式(比如写 SQL )操作数据集。

以你的例子为例。
你的业务需要联表,但库表并没有真实的外键与一对多关系。

在这种情况下,以我的习惯,我更偏向于通过 Service 解决问题。
既 List<User> a = DAOFunc1(xxx);List<Group> b = DAOFunc2(xxx);
然后 a 与 b 再通过 LINQ 、遍历等等方式取交、并、补。

但这并不一定正确。理论上要根据你当前使用的语言、数据库性能特性而定。

实际上则更复杂一些,有可能做 service 的人工作量比较大,就需要做 DAO 的人代劳,有可能实现 Service 的人是 DAO 实现者的上级 /下级,导致推活儿给下级等等。

至于说“DAO 100 行都写什么呢”,比如我目前负责的产品的后端代码,经常有超大 SQL 的 DAO 出现。(本人数据库白·痴,不评判好坏😂)
chaleaoch
2021-09-09 17:31:20 +08:00
@FreeEx 学到了.
因为是 python 后端入门, Java 生态还停留在理论层面, 代码量有限. 想请教:

"DAO 是面向数据的,经常会写一些多表的 sql,关于 db 的业务逻辑也包含在了 dao 里面。"

所以说 某些情况下, 是不是一个 Dao 理论上可以对应多个 Model.

就是说 我的 DAO 层抽象可以把 Model 层忽略掉. service 别管数据从哪里来, 你就按照我 DAO 的设计获取数据就可以了.
那么就像你说的, UserDAO.getAllData() 可能跨了十张表查询出一个数据, 把数据丢给 service. 甚至于这里面可能还带了一些业务. 譬如,
users_data 和 group data

for group in groupLst:
if users_data.id = group.id:
balabala

谢谢
FreeEx
2021-09-09 18:10:55 +08:00
@chaleaoch 是这个意思

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/775162

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX