V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
waibunleung
V2EX  ›  程序员

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

  •  1
     
  •   waibunleung · 2021-05-06 14:22:54 +08:00 · 7962 次点击
    这是一个创建于 1326 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 分别是什么层面上的东西?

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

    78 条回复    2021-09-09 18:10:55 +08:00
    ychost
        1
    ychost  
       2021-05-06 14:27:40 +08:00
    可以直接把 repository 注入到 Service 里面用,对于一些复杂的手工 SQL,包一个 Repository 再给 Service 用
    waibunleung
        2
    waibunleung  
    OP
       2021-05-06 14:33:26 +08:00
    @ychost 有 demo 吗?
    waibunleung
        3
    waibunleung  
    OP
       2021-05-06 14:35:18 +08:00
    @ychost 这里面说的 Repository 就是 DAO 层?
    kop1989
        4
    kop1989  
       2021-05-06 14:45:58 +08:00   ❤️ 7
    DAO,一般叫数据访问层。
    ORM,说的是对象关系映射。

    这二者的描述角度不同。
    一个是从控制操作角度描述,一个是从模型设计角度描述。

    DAO 指的是对数据库表操作的方法封装。
    而 ORM 指的是实体(类)与数据库表之间的对应关系。

    所以:
    1 、有了 ORM,是否还需要 DAO ?不一定,取决于你的设计,以及各个语言的 ORM 框架功能不同而决定。

    举例:比如我需要输出表 A 的行数,如果是单纯的 ORM,那我就只能直接获取本表的所有内容,返回一个 List<A>,然后我在业务层再去获取 List<A>的 size 。这明显是效率非常低下的。于是我就可以通过 DAO 层单独编写封装 SQL 去获取表 A 的行数,返回。

    2 、DAO 和 ORM 冲突么?不冲突。
    Rwing
        5
    Rwing  
       2021-05-06 14:57:19 +08:00
    DAO 是代码分层的概念。
    DAO 中使用 ORM 来进行数据访问,举个例子,假如某一天不想用 ORM 了,只需要替换掉 DAO 层中的实现即可。
    题外话:其实如果用来写业务,GO 并不比其他语言拥有更好的开发效率。想不明白为什么这么多人转 GO
    bigpigeon
        6
    bigpigeon  
       2021-05-06 15:00:21 +08:00
    我觉得看项目复杂度,如果一个项目 2 个人写,可以一个人负责数据访问一个人负责业务对接,那就应该分一层 DAO 出来,如果是一个人用了 ORM 就没必要搞一个 DAO 出来,不然你改起来会贼蛋疼,个人经验所得 ,顺手推下以前写的 ORM https://github.com/bigpigeon/toyorm
    waibunleung
        7
    waibunleung  
    OP
       2021-05-06 15:11:47 +08:00
    @kop1989 你的意思是,DAO 层可以通过 ORM 来实现?那 dao 层实现的函数一般类似怎么样的呢?是类似于这样的函数吗?
    ```php
    function queryByContentAndCreatedBy($content, $createdBy){
    $sql = "SELECT * FROM content WHERE title like '%".$content."%' AND created_by=?";
    $sqlQuery = new SqlQuery($sql);
    $sqlQuery->setNumber($createdBy);
    return $this->getList($sqlQuery);
    }

    ```
    还是只是封装了简单的 CURD ?就是之前没有用过(或者用过不知道),看看看具体的 dao 实现是什么样的,或者说什么样的函数适合写到 dao 中。
    kop1989
        8
    kop1989  
       2021-05-06 15:19:46 +08:00
    @waibunleung #7 没错,是这个意思。DAO 层可以通过 ORM 实现增删改查,也可以不通过。

    至于说需要封装什么方法,这就要看你的程序业务需求而定了。
    最基础的当然是增删改查。

    关键在于多一层封装,就把数据控制逻辑和业务逻辑解耦了。
    以后更换数据控制方法,比如从 ORM 变成 SQL,或者从 SQL 变成 ORM,甚至是更换数据库类型等,都不会对上层业务造成影响。
    waibunleung
        9
    waibunleung  
    OP
       2021-05-06 15:20:37 +08:00
    @Rwing dao 层通过 orm 来访问数据的话,那 dao 层的实现一般长什么样子呢?另外如果 dao 层的实现不够通用,那不是一种复杂的查询就要对应一个函数?这样 dao 里面每来一个复杂查询都新增一个函数吗?
    kop1989
        10
    kop1989  
       2021-05-06 15:20:44 +08:00
    @waibunleung #7 这也就衍生出来说:DAO 层暴露出来的接口设计,一定要是业务驱动的,或者说是功能驱动的,而不能是技术驱动的。
    waibunleung
        11
    waibunleung  
    OP
       2021-05-06 15:27:53 +08:00
    @kop1989 dao 的实现一般是怎么样的?能否举个例子呢?说增删改查很模糊,没有具体感念
    waibunleung
        12
    waibunleung  
    OP
       2021-05-06 15:31:02 +08:00
    @bigpigeon 分一层 dao 出来,那 dao 里面具体些什么样的逻辑呢?能不能举个例子呢?
    bthulu
        13
    bthulu  
       2021-05-06 16:07:58 +08:00
    orm 就是封装了一些 crud, 原本需要你手动 set 参数的, 你传个对象进去, 就根据对象字段名帮你映射到 sql 语句的?上. 然后再帮你把返回的数据按列名映射到 java 类字段上.
    dao 就是原本你的 crud 直接写在业务方法里, 现在有人说 no, 你这样写不 cool, 你要把 crud 全部写到一个单独的类里, 这样 cool.
    xiangwan
        14
    xiangwan  
       2021-05-06 16:22:24 +08:00
    这个层怎么分,可以多看看那几个架构模式。什么 DDD 、洋葱、六边形、清晰等等。
    DAO 是比较旧时代的水平分层里的概念
    newtype0092
        15
    newtype0092  
       2021-05-06 16:52:06 +08:00
    用用户信息举例:

    DAO 屏蔽了数据获取的逻辑,只提供一个 getUser(id) 的方法,不管里面怎么实现,需要数据用这个方法取就行。

    然后看 DAO 内部,可以用很多方法去实现。

    最简单的方法,直接查库,query('select * from users where id = ' + id),然后返回数据,需要什么数据就直接写 SQL
    ORM 就是把常用的 SQL 封装好,这样用里面提供的类似 getUserById(id) 的方法就能省掉上面的字符串拼接、参数处理、校验、返回数据格式化等问题,直接返回映射好的 object 。

    当然,不一定所有数据都存在关系数据库里,也可能存在 nosql 数据库、磁盘文件甚至需要通过网络请求其他服务,但不管存在哪,为了最大限度的减少这些乱七八糟的逻辑对业务逻辑的影响,只提供统一的操作数据的接口,这个就是 DAO 干的活。
    waibunleung
        16
    waibunleung  
    OP
       2021-05-06 17:05:43 +08:00
    @bthulu 那有了 orm 就不需要 dao 了吧?
    zjsxwc
        17
    zjsxwc  
       2021-05-06 17:11:08 +08:00
    当然是全都要,两者又不排斥。

    ORM 是关系数据库表中每一行与面向对象语言每个实体类实例的映射。
    DAO 是设计模式,把同类通表数据库操作都整理到一个类中。

    实际上,ORM 的 Repository 就是 DAO 。

    就是这么简单。
    waibunleung
        18
    waibunleung  
    OP
       2021-05-06 17:12:59 +08:00
    @newtype0092 假如我有一个根据用户活跃度排序输出用户列表的需求,此时 orm 并没有直接的方法,我就需要在 dao 层搞一个类似 getUsersByActiveOrder(limit, offset)这样的方法?那每来一个复杂的查询就需要在 dao 层增加一个方法,不管我 dao 的方法里面写的是原始 sql 还是用 orm,是这个意思吗?
    waibunleung
        19
    waibunleung  
    OP
       2021-05-06 17:14:01 +08:00
    @zjsxwc Repository 这个里面是怎么样的?有简单的例子举一下吗?
    zjsxwc
        20
    zjsxwc  
       2021-05-06 17:14:41 +08:00
    大概也就只用过 ActiveRecord 这种没有 Repository 的 ORM 的人,会对 ORM 与 DAO 有疑惑吧
    labulaka521
        22
    labulaka521  
       2021-05-06 17:25:48 +08:00 via iPhone
    dao 差不多就是包装了 curd 的一些方法吧,比如每次查询都会自动加上一些 id 、deletetime 等信息,然后接口更加的具体化,比如说 saveUser updateUser getUser 等
    waibunleung
        23
    waibunleung  
    OP
       2021-05-06 17:32:00 +08:00
    @zjsxwc 如果要说 Repository 就是 dao 的实现的话,那我们之前只是将 Repository 层的函数,放到了 model 层去,没有分这一层出来,这样 model 就 = model + dao 了,那为什么不将 dao 合并到 model 这一层呢?
    waibunleung
        24
    waibunleung  
    OP
       2021-05-06 17:33:14 +08:00
    @labulaka521 这样看起来这些接口是业务驱动的,而不是用 orm 再封装一层
    newtype0092
        25
    newtype0092  
       2021-05-06 17:40:04 +08:00
    @waibunleung #18 是的,正常来说,把所有数据操作封装在一起( DAO 类),好处远远大于开销,比如:

    能方便的复用之前的逻辑,不用再复制粘贴一遍
    可以快速查看在当前数据集上有哪些操作
    调整下层数据结构或添加删除参数时不用去找每一个调用的地方
    需要整个替换掉 ORM 时改动不会太大
    测试时方便 mock 数据
    等等等。

    而开销仅仅是多几个 DAO 类。

    哪怕只是为了让代码结构更清晰,这几个类都是有必要的。
    newtype0092
        26
    newtype0092  
       2021-05-06 17:48:43 +08:00
    @waibunleung #23 简单的项目结构里 model 充当 DAO 的设计很合理,轻便且能满足大部分需求
    zjsxwc
        27
    zjsxwc  
       2021-05-06 18:14:58 +08:00 via Android
    @waibunleung
    用 repository 当然是为了通过依赖注入容器搞黑魔法了,比如运行 aop 面向切面编程。

    如果都写到实体类 entity (也就是你说的 model )中
    zjsxwc
        28
    zjsxwc  
       2021-05-06 18:16:08 +08:00 via Android
    就相当于写死了,有 1 万个实体类,难道注入同样的东西一万遍?
    zjsxwc
        29
    zjsxwc  
       2021-05-06 18:19:18 +08:00 via Android
    如果不用实体类实例方法搞,而用是在实体类的中用静态方法就更惨,要修改静态方法的行为怕是得 jvm 底层修改了,而显然我们动不了 jvm 代码。
    waibunleung
        30
    waibunleung  
    OP
       2021-05-06 18:27:58 +08:00
    @zjsxwc repository 通过依赖注入到容器,有没有例子呀?
    zjsxwc
        31
    zjsxwc  
       2021-05-06 18:37:33 +08:00 via Android
    @waibunleung

    这个你去看看 springboot 或者 symfony 的容器相关文档吧
    waibunleung
        32
    waibunleung  
    OP
       2021-05-06 19:22:42 +08:00
    @zjsxwc 不对啊,如果像你说的要用 repository 依赖注入到容器,那容器调用的时候不需要统一 repository 的接口方法吗?比如我 UserRepository 有 queryList 方法,ContentRepository 却没有 queryList 方法,那注入到容器的时候,不就会报错?
    zjsxwc
        33
    zjsxwc  
       2021-05-06 19:40:43 +08:00 via Android
    @waibunleung
    不同的类注入,当然是各自的类型了,不会有你说的情况。
    di94sh
        34
    di94sh  
       2021-05-06 20:06:52 +08:00 via iPhone
    dao 还是很有必要的,项目大了后 orm 对象满天飞 的话无法重构,单元测试也不好写,业务逻辑也不容易理清
    waibunleung
        35
    waibunleung  
    OP
       2021-05-06 20:24:01 +08:00
    @zjsxwc symfony 的 容器注入,不是没有类型吗?
    zjsxwc
        36
    zjsxwc  
       2021-05-06 20:51:25 +08:00 via Android
    @waibunleung
    手动注入需要类型 class
    自动注入 autowire 会根据参数类型自动确定
    fkdog
        37
    fkdog  
       2021-05-06 21:59:27 +08:00
    @Rwing 大部分的项目其实短平快方式效率更高。

    设计模式、项目架构一类东西有时候过于工业化和笨重了。

    特别是在国内,大部分互联网公司并不会太过于在意项目的可扩展性和维护性。为了跟进对手或者抢占市场都是以先实现功能为主,反正用的 java,代码写的再烂也能勉强调试维护下去。真的维护不动了就推翻重构或者重写。

    如果项目竞争不过对手的话,这个项目可能还没到需要重写的地步可能就被关闭了。所以在国内的话,什么代码整洁、项目结构合理之类的东西并不能产生价值。

    国外的话情况可能会不一样做项目就跟建房子一样,一步一步稳稳的来。像美帝那边的大厦都上百年了,国内的话可能三四十年就拆了重建吧。
    THP301
        38
    THP301  
       2021-05-06 22:01:02 +08:00
    生搬硬套不一定合适
    janus77
        39
    janus77  
       2021-05-06 22:05:43 +08:00
    ORM 基本上是单纯的从 db 里面拿数据
    有些情况下需要对数据进行组装和变换,这些操作按规范是不放在 ORM 的,于是就多出一层 Dao
    但是由于 java 的“过于规范化”(这点我觉得不完全是坏事,因场景而异),所以不需要 dao 处理的情况也需要先经过一层 dao,这是为了统一。
    FreeEx
        40
    FreeEx  
       2021-05-06 22:41:40 +08:00
    前段时间为了学习 Go 写了个开源项目,分了 service 层和 repository 层,repository 主要是用来操作某一个表的,例如根据 ID 查询可能会很多地方用到,repository 可以写一个 FindById 函数,参数是 ID,返回是数据库表的结构体,这样就不必每次都去拿全局 DB.Where("id = ?", id).First(&User{}) 查询了,也更方便检索有哪些地方用了这个函数。service 层主要也是写一个会复用到的函数,不复用的直接就用 repository 操作了。
    项目地址是: https://github.com/dushixiang/next-terminal
    代码写的比较水,楼主可以参考下。
    catcn
        41
    catcn  
       2021-05-06 23:22:22 +08:00
    orm 的定义基本就是 Model 里边对数据库的一个映射啦。dao 就是一个事务里边的的组合体就好啦。上边再来一个 service,基本就成型了。web 的话,还有一个 handler 的层。
    charlie21
        42
    charlie21  
       2021-05-06 23:46:57 +08:00
    @waibunleung #30
    https://xblog.lufficc.com/blog/the-core-conception-of-laravel
    https://segmentfault.com/a/1190000004875930

    看 UserRepository 是如何注入到 UserController ( 放 __construct 当参数,框架的 IoC 机制会将它自动注入) 里的。
    这里 UserRepository 就是一个服务,UserController 就是消费这个服务的消费者。生产者消费者关系(谁是 service,谁在消费 service )搞清楚就 OK 了:有个生产者消费者的概念就 OK 了

    关于 “分层目的是方便出错排错 / layered architecture 好在哪里” 的文章
    https://learnku.com/articles/48397
    https://learnku.com/articles/5813/overview-of-hierarchical-model-for-web-pyramid-development-framework
    https://docs.microsoft.com/en-us/dotnet/architecture/modern-web-apps-azure/common-web-application-architectures
    bthulu
        43
    bthulu  
       2021-05-07 08:34:24 +08:00
    @waibunleung 看语言表达能力的, 像 java 这种假泛型, 一般还是得针对每个表建立一个 dao 类. 真泛型像 c#这种, 就不用这么麻烦
    vicalloy
        44
    vicalloy  
       2021-05-07 09:23:19 +08:00
    主要还是看 ORM 的表现能力,如果 ORM 的表现能力不足,使用 DAO 封一下可用让代码更为清晰。
    如果 ORM 表现能力足够强,加 DAO 更多的是 Java 设计模式看的太多,有些生搬硬套。

    注:
    Python 和 Ruby 相关的应用就没看到有谁去加一层 DAO 。
    pkoukk
        45
    pkoukk  
       2021-05-07 09:46:19 +08:00
    orm 比较纯粹,一个 sturct 一般就是对应一张表
    DAO 比较复杂,提供出来的函数中可能包含了多个 orm
    纠结名词意义不大,自己写写就知道了
    waibunleung
        46
    waibunleung  
    OP
       2021-05-07 10:00:16 +08:00
    @pkoukk 在写,就是觉得 ORM 用着挺方便的,加一层 DAO 是有好处,但是看着好像不太大的样子
    konakona
        47
    konakona  
       2021-05-07 10:12:24 +08:00
    DAO 里的代码看一看你就能发现它跟 ORM 的 Repository 部分的作用是一样的,封装了对模型的操作访问方法。

    ORM 是对数据的映射关系,gorm 就是这个作用,gorm 一开始会写类型声明和映射关系,类似 Laravel eloquent 一开始声明和 hasmany()那些。

    都可以对号入座的。
    waibunleung
        48
    waibunleung  
    OP
       2021-05-07 10:23:58 +08:00
    @konakona 所以是有了 orm 之后其实不需要 dao 层了?
    waibunleung
        49
    waibunleung  
    OP
       2021-05-07 10:38:40 +08:00
    @catcn 组合体怎么理解?
    waibunleung
        50
    waibunleung  
    OP
       2021-05-07 11:23:00 +08:00
    @konakona 问题是不知道 dao 里面写的什么代码?!
    konakona
        51
    konakona  
       2021-05-07 13:28:47 +08:00
    @waibunleung

    我有个项目可以给你简单参考下我当初是怎么理解和怎么做的:
    dto 看这里 https://github.com/54853315/weapp-MrsZhangPrivateKitchen-Backend/tree/master/pkg/api
    引用这些 dto 的 orm 看这里 https://github.com/54853315/weapp-MrsZhangPrivateKitchen-Backend/tree/master/models
    konakona
        52
    konakona  
       2021-05-07 13:30:25 +08:00
    补充下上条:
    由于我大部分 dto 比较“偷工减料”,dto 你看这一份: https://github.com/54853315/weapp-MrsZhangPrivateKitchen-Backend/blob/master/pkg/api/dto/user.go

    然后再看下 user 控制器和 user model 是怎么与它交互的就差不多够了。
    konakona
        53
    konakona  
       2021-05-07 13:31:26 +08:00
    waibunleung
        54
    waibunleung  
    OP
       2021-05-07 15:12:11 +08:00
    @konakona 看了一下,你的 model 层除了定义了实体类型以外,还在此层基于 orm 封装了一些数据存取函数,这样看你的 model 像是 DAO + model,那你的 DTO 的职责只是定义了数据传输的对象结构,不知道我这样理解有没有错
    dayudayupao
        55
    dayudayupao  
       2021-05-07 15:59:29 +08:00
    dao 可以跟 orm 共存啊,dao 作为在 mvc 设计模式下的数据访问层,这个数据到底是谁提供的,mysql?redis?mongo?hbase?
    其实不关心啊,orm 作为一种特定数据存储组件的一个封装而已,对业务来说其实可以是黑盒;其实日常情况下,dao 可以封装缓存层,比如将 mysql 的数据拉到 redis 缓存,业务逻辑层无需关注是怎么缓存的,交给 dao 做就好了
    waibunleung
        56
    waibunleung  
    OP
       2021-05-07 16:06:57 +08:00
    @dayudayupao 所以 dao 层可以用 orm 去搞对吧?写的函数就类似于我 7 楼写的那样?
    passerbytiny
        57
    passerbytiny  
       2021-05-07 16:08:43 +08:00 via Android
    dao 是一种业务逻辑层,orm 是一种基础设施,它俩是两码事。这 ORM 还只是仅参与 DAO 层的基础设施,你要是碰到贯穿所有层次的基础设施,比如 Java 的 Spring,要是还想分层,估计直接死翘翘。
    dayudayupao
        58
    dayudayupao  
       2021-05-07 16:27:01 +08:00
    @waibunleung 写法都是灵活的,就像 @kop1989 所说的那样,dao 方法定义是业务驱动,只关心我要什么,而在 dao 里,需要写 sql 的时候就写,不需要写比如 orm 封装了就不用写,看 orm 来定,也就是说 dao 里面跟 orm 是强耦合的,你需要对特定的 orm 做出适配,这些都是 dao 做的事. 综合来看如果你底层数据库组件有可能变化,或者不确定会不会变化,就可以 dao,orm 都共存.
    在我看来,不管什么情况,共存看起来层次会分明点,即使是透传.
    konakona
        59
    konakona  
       2021-05-07 17:10:25 +08:00
    @waibunleung 兄弟,你的理解不太对。

    我在 Model 里做的事情就是 eloquent 里会做的事情。
    我在 DTO 里做的事情就是 Repository 里会做的事情。
    waibunleung
        60
    waibunleung  
    OP
       2021-05-07 20:03:21 +08:00
    @konakona 这又涉及到了 eloquent 和 Repository 的 区别了...
    你在 model 里面定义了一些数据操作的函数,在 DTO 里面只有数据传输对象,我是这么理解的...

    eloquent = 数据表映射 + 数据操作
    如果你用了 Repository pattern,那数据操作应该放在 Repository,eloquent 仅仅是一些数据表映射,属性定义就好了
    bsg1992
        61
    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
        62
    waibunleung  
    OP
       2021-05-07 22:28:36 +08:00
    @bsg1992 最后一点我可能不太认同,不想 orm 在 service 层满屏飞,还是在 dao 封装一层好一点,即使是简单的调用
    meshell
        63
    meshell  
       2021-05-08 10:18:02 +08:00
    @waibunleung symfony 的 容器注入实际就是反射构造函数的参数,然后注入的。laravel 了是一样的
    bsg1992
        64
    bsg1992  
       2021-05-08 10:33:27 +08:00
    @waibunleung 这样的封装 会导致失去 ORM 很多的特性,而且会导致 DAO 层越来越臃肿 出现很多 FindByXX 之类的 func
    waibunleung
        65
    waibunleung  
    OP
       2021-05-08 13:55:05 +08:00
    @bsg1992 那想请教下,你认为 orm 和 dao 共存的时候,具体的使用场景是怎么样的?什么时候直接用 orm 进行链式调用,什么时候用 dao 呢?可不可以举个例子区分一下呢?十分感谢!
    VeeSong
        66
    VeeSong  
       2021-05-12 09:04:58 +08:00 via Android
    dao 层获取一个对象(或一堆数据),至于这个对象怎么获得的,用 MySQL 、MongoDB 、Redis 、ES 还是文本文件,调用 dao 的人并不关心。如果用 MySQL 就用 MySQL 的 orm (如 Java 的 hibernate,PHP 的 fluent ),如果用 Redis 就用 Redis 的 orm 。
    以后你想重构为其他任何的数据层 orm,调用 dao 的人都不需要做任何改动。
    VeeSong
        67
    VeeSong  
       2021-05-12 09:27:15 +08:00 via Android
    在 Java 中,dao 一般是先定义接口类,然后写对应的实现类。实现类可以是任何 orm,然后通过 spring 注入你想要的 dao 实现类。无论之后别人想换任何奇奇怪怪的 orm 都没问题,只要照着接口类去实现,然后用 Junit 跑一遍测试,测试 OK 的话修改下 spring 的注入配置就行了。
    dao 用接口的形式封装,对测试和重构都非常友好
    waibunleung
        68
    waibunleung  
    OP
       2021-05-13 16:30:59 +08:00
    @VeeSong 明白,这是我期待的答案
    decimalbell
        69
    decimalbell  
       2021-05-14 17:16:54 +08:00
    考虑这个场景,查询用户信息,先从 Redis 缓存里面查,查不到再从数据库里面查
    一般有如下文件:
    user_dao.go
    user_redis.go
    user_db.go
    你一直纠结的 ORM 只是在 db 这一层,dao 其实是更大的概念
    waibunleung
        70
    waibunleung  
    OP
       2021-05-27 17:29:32 +08:00
    @decimalbell 这三个文件里面写的是怎样的代码呢?可以举一个更详细的例子吗?
    chaleaoch
        71
    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
        72
    kop1989  
       2021-09-09 14:29:28 +08:00   ❤️ 1
    @chaleaoch #71

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

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

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

    想听听你的见解.


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

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

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

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

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

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

    那 DAO 里面的方法 100 行 都写些什么呢? 如果没有业务的话 没啥可写的啊.
    FreeEx
        75
    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
        76
    kop1989  
       2021-09-09 17:11:17 +08:00   ❤️ 1
    @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
        77
    chaleaoch  
       2021-09-09 17:31:20 +08:00   ❤️ 1
    @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
        78
    FreeEx  
       2021-09-09 18:10:55 +08:00
    @chaleaoch 是这个意思
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2871 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 14:16 · PVG 22:16 · LAX 06:16 · JFK 09:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.