数据访问对象 (DAO) 是否应该委托给其他 DAO 以提高可读性?

Should Data Access Objects (DAO) delegate to other DAOs for readability?

将一个 DAO 的某些部分委托给另一个 DAO 是否更好,这样它会更具可读性和紧凑性?以及减少重复代码?

示例

class StudentDAOImpl implements StudentDAO {
   public Student findById(int studentId) {
       // some code here to get student
   }
}

class SemesterEnrollmentImpl implements SemesterEnrollmentDAO {
   public SemesterEnrollment findSemesterEnrollmentByStudent(int studentId) {
       // delegating finding of student to StudentDAO
       StudentDAO studentDao = new StudentDaoImpl();
       Student student = studentDAO.findById(studentId);

       // code to get SemesterEnrollment using student instance
   }
}

因为我读到 DAO 应该:

这是否意味着我必须在每个 DAO 中重复相同的过程,而不是委托,这样它就可以独立于其他 DAO?

一个常见的事情是使用服务来调用不同的 daos 来注入你的数据。在这种情况下,这似乎更合乎逻辑,因为它实际上是业务逻辑,在我看来,最好在数据库层中尽可能少地使用它

根据经验,DAO 应该关注 retrieving/persisting 数据,因此任何业务逻辑或数据转换通常都驻留在单独的业务层中。在您提供的示例中,人们可能会问,如果由于根本没有数据而导致对 StudentDAO 的调用失败,会发生什么情况。这就是在服务层解决的问题。 在您的示例中,EnrollmentDAO 可以变成 EnrollmentService。

在您的示例中,这里的主要问题是您正在使用 SemesterEnrollmentDAO 搜索另一个实体。

绝对 需要检索 Student 实体以便稍后检索 SemesterEnrollment 吗?

如果您的实体之间的关系设计得很好,您可以使用 studentId 查询 SemesterEnrollment,而不必先检索 Student 实体。

例子

Query query = em.createQuery("SELECT e FROM SemesterEnrollment e JOIN e.student s "
                            + "WHERE s.studentId = :studentId");

如果您真正需要检索的是 SemesterEnrollment 实体,那么您可以通过这种方式避免委派和 1+N 问题,SemesterEnrollmentDAO.

简答:否

根据模式

,每个class可以被称为Table Module的DAO限制为一个table

A Table Module organizes domain logic with one class per table in the data-base, and a single instance of a class contains the various procedures that will act on the data

但是,这些可以用外观或 Transaction Script to manage the interaction between multiple Table Modules 包裹。

A Transaction Script organizes all this logic primarily as a single procedure, making calls directly to the database or through a thin database wrapper. Each transaction will have its own Transaction Script, although common subtasks can be broken into subprocedures.

您知道经验法则,但没有人解释其中的原因。

DAO 的作用是包含一组 CRUD 功能。每个方法都是针对事务数据存储(通常是数据库)的一些操作。如果将 DAO 调用链接在一起,最终会将事务链接在一起。这对 READS 来说不是什么大事;但是当你进入 WRITES 时,它会变得一团糟。

例如,您有一个复合 EnrollmentRecord 对象。它包含一个 StudentRecord,每个 class 学生注册的 SectionRecords,每个 SectionRecord 链接到一个 ClassRecord。如果您调用 createEnrollement(EnrollmentRecord),它还可以链出以将其他对象包括在 EnrollmentRecord 中。

如果您可以创建 StudentRecord,但部分 SectionRecords 或 ClassRecords 失败,会发生什么情况?回滚吗?您离开学生,但删除所有已注册的 classes 吗?如果一个注册 class 失败了,你会把它们都回滚吗?

一旦您开始协调事务,您现在就在混合业务逻辑。业务规则会随着时间和情况而改变;但是 Enrollment、Student、Section 和 Class 的基本 CRUD 不会。

使用服务对象聚合 DAO。复合数据对象应该由协调所有 DAO 调用的服务管理(事件对单个 DAO 的多次调用!)这允许您将 business/transaction 逻辑与 CRUD 逻辑分开,并保持每一层模块化和可重用。

使用服务层的另一个重要原因是实体模型通常是双向的。您可以加载一个学生,然后获取他们所有的 classes。您可以获取 class 并加载其所有学生。随着对象模型的增长,您将很容易获得循环关系。

例如,您有 objectA,其中包含 objectB 的集合。您的 loadA 方法调用 loadB.

以后有人加扩充loadB委托给LoadC。后来,有人说如果我可以加载一个 'C' 并知道它链接到哪个 'A' 就好了。因此,他们扩展了 loadC 以委托给 returns 一个 'A' 对象的 loadA。现在整个事情爆炸了,如果不跟踪整个圆形路径就不明显了。

使用服务层来协调复合对象。将组合逻辑与 CRUD 逻辑分离,可以更轻松地避免循环引用和管理复杂的对象图。

所以 DAO 调用 DAO 本身并不是邪恶的;但是,这种捷径很容易导致处理 CRUD、business/transaction 逻辑和复杂对象图规则的巨大 DAO 方法。将 DAO 与服务完全分离有助于防止这种情况。