数据访问对象 (DAO) 是不是应该委托给其他 DAO 以提高可读性?
Posted
技术标签:
【中文标题】数据访问对象 (DAO) 是不是应该委托给其他 DAO 以提高可读性?【英文标题】:Should Data Access Objects (DAO) delegate to other DAOs for readability?数据访问对象 (DAO) 是否应该委托给其他 DAO 以提高可读性? 【发布时间】:2015-09-29 08:33:45 【问题描述】:将一个 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 应该:
N + 1 问题。 尽量减少 SQL 调用的次数。 DAO 不应委托给其他 DAO。这是否意味着我必须在每个 DAO 中重复相同的过程,而不是仅仅为了使其独立于其他 DAO 而进行委派?
【问题讨论】:
【参考方案1】:一个常见的事情是使用服务来调用不同的 daos 需要注入您的数据。在这种情况下,这似乎更合乎逻辑,因为它实际上是业务逻辑,而且 imo 最好在数据库层中尽可能少地使用它
【讨论】:
我的另外一个优点是,DAO 应该可以跨用例重用,服务方法应该实现单独的详细级用例。【参考方案2】:简短回答:否
可以调用Table Module的DAO根据模式限制为每个类一个表
一个表模块用数据库中的每个表一个类来组织域逻辑,一个类的单个实例包含将作用于数据的各种过程
但是,这些可以由外观或Transaction Script 包装,以管理多个Table Modules 之间的交互。
事务脚本主要将所有这些逻辑组织为单个过程,直接调用数据库或通过瘦数据库包装器。每个事务都有自己的事务脚本,尽管常见的子任务可以分解为子过程。
【讨论】:
【参考方案3】:您知道经验法则,但没有人解决其中的原因。
DAO 的作用是包含一组 CRUD 函数。每个方法都是针对事务数据存储(通常是数据库)的一些操作。如果您将 DAO 调用链接在一起,您最终会将交易链接在一起。这对 READS 来说没什么大不了的。但是当你进入 WRITES 时,它会变得一团糟。
例如,您有一个复合 EnrollmentRecord 对象。它包含一个 StudentRecord,学生注册的每个班级的 SectionRecords,每个 SectionRecord 链接到一个 ClassRecord。如果您调用 createEnrollement(EnrollmentRecord),它还可以链出以将其他对象包含在 EnrollmentRecord 中。
如果您可以创建 StudentRecord,但某些 SectionRecords 或 ClassRecords 失败,会发生什么情况?回滚吗?您是否离开了学生,但删除了任何已注册的课程?如果一个注册的课程失败了,你会全部回滚吗?
一旦您开始协调事务,您现在就在混合业务逻辑。业务规则会随着时间和情况而改变;但 Enrollment、Student、Section 和 Class 的基本 CRUD 不会。
使用服务对象来聚合 DAO。复合数据对象应该由一个服务来管理,该服务协调对 DAO 的所有调用(事件对单个 DAO 的多次调用!)这允许您将业务/事务逻辑与 CRUD 逻辑分开,并保持每一层的模块化和可重用性。
使用服务层的另一个重要原因是实体模型通常是双向的。您可以加载一个学生,然后获取他们的所有课程。您可以获取一个班级并加载其所有学生。随着对象模型的增长,您将很容易获得循环关系。
例如,您有 objectA,其中包含 objectB 的集合。您的 loadA 方法调用 loadB。
将来有人添加扩展 loadB 以委托给 LoadC。后来,有人说如果我能加载一个“C”并知道它链接到哪个“A”,那就太好了。因此,他们扩展 loadC 以委托给返回“A”对象的 loadA。现在整个事情都爆炸了,如果不追踪整个圆形路径,它并不明显。
使用服务层来协调复合对象。将组合逻辑与 CRUD 逻辑分离,可以更轻松地避免循环引用和管理复杂的对象图。
所以 DAO 调用 DAO 本身并不是邪恶的;但是,这种捷径很容易导致处理 CRUD、业务/事务逻辑和复杂对象图规则的大型 DAO 方法。将 DAO 与服务完全分离有助于防止这种情况发生。
【讨论】:
【参考方案4】:在您的示例中,这里的主要问题是您正在使用 SemesterEnrollmentDAO 搜索另一个实体。
您绝对是否需要检索Student
实体以便稍后检索SemesterEnrollment
?
如果您的实体之间的关系设计得很好,您可以使用studentId
查询SemesterEnrollment
,而不必先检索Student
实体。
例子
Query query = em.createQuery("SELECT e FROM SemesterEnrollment e JOIN e.student s "
+ "WHERE s.studentId = :studentId");
如果您真正需要检索的是SemesterEnrollment
实体和SemesterEnrollmentDAO
,则通过这种方式可以避免委托和1+N 问题。
【讨论】:
【参考方案5】:根据经验,DAO 应该关注检索/持久化数据,因此任何业务逻辑或数据转换通常都驻留在单独的业务层中。在您提供的示例中,如果由于根本没有数据而对 StudentDAO 的调用失败,会发生什么情况。这就是在服务层解决的问题。 在您的示例中,EnrollmentDAO 可以转换为 EnrollmentService。
【讨论】:
以上是关于数据访问对象 (DAO) 是不是应该委托给其他 DAO 以提高可读性?的主要内容,如果未能解决你的问题,请参考以下文章