是否将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 ?
一种常见的做法是使用服务来调用不同的dao来注入你的数据。在这种情况下,这似乎更合乎逻辑,因为它实际上是业务逻辑,在我看来,在您的数据库层中尽可能少地使用它
简短的回答:不
可以称为表模块的dao根据模式
将每个类限制为一个表Table Module用数据库中的每个表一个类来组织域逻辑,一个类的单个实例包含将对数据起作用的各种过程
然而,这些可以被facade或Transaction Script包装,以管理多个Table module之间的交互。
事务脚本将所有这些逻辑主要组织为一个过程,直接调用数据库或通过瘦数据库包装器。每个事务都有自己的transaction Script,尽管普通的子任务可以分解成子过程。
你知道经验法则,但没有人指出原因。
DAO的角色是包含一组CRUD函数。每个方法都是对事务性数据存储(通常是数据库)的一些操作。如果你把DAO调用链在一起,你最终会把交易链在一起。这对read来说不是什么大事;但是当你开始写的时候,它就变得混乱了。
例如,您有一个复合的enrollment mentrecord对象。它包含学生所注册的每个班级的StudentRecord、SectionRecords,每个SectionRecord链接到一个ClassRecord。如果调用createenroll (registrmentrecord),它也可以链出,以包括registrmentrecord中的其他对象。
如果你可以创建StudentRecord,但一些SectionRecords或ClassRecords失败会发生什么?要回滚吗?你是否离开了学生,但取消了所有已注册的课程?如果一门课不及格,你会把所有的课都退吗?
一旦你开始协调事务,你现在就混合了业务逻辑。业务规则将随着时间和情况的变化而变化;但是基本的注册、学生、分组和班级的CRUD则不会。
使用Service对象聚合dao。组合数据对象应该由一个服务来管理,该服务协调对DAO的所有调用(对单个DAO的多个调用!)这允许您将业务/事务逻辑与CRUD逻辑分离,并保持每个层的模块化和可重用性。
使用服务层的另一个重要原因是实体模型通常是双向的。你可以加载一个学生,然后获取他们所有的类。你可以获取一个类并加载它所有的学生。随着对象模型的增长,您将很容易获得循环关系。例如,您有一个objectA,它包含一个objectb的集合。你的loadA方法调用loadB。
将来有人添加扩展loadB来委托给LoadC。后来,有人说,如果我能加载一个"C",并知道它链接到哪个"a",那就太好了。它们展开loadC去委托给那个返回一个A对象的loadA。现在整个东西爆炸了,如果不沿着整个圆形路径,它就不明显了。
使用Service层来协调组合对象。将组合逻辑与CRUD逻辑分离可以更容易地避免循环引用和管理复杂的对象图。
所以dao调用dao本身并不邪恶;但是,这是一种很容易导致处理CRUD、业务/事务逻辑和复杂对象图规则的大型DAO方法的捷径。将dao与服务彻底分离有助于防止这种情况的发生。
在您的示例中,这里的主要问题是您正在使用semesterregistrmentdao来搜索另一个实体。
您是否绝对需要检索Student
实体以便稍后检索SemesterEnrollment
?
如果您的实体之间有一个精心设计的关系,您可以对SemesterEnrollment
进行查询,使用studentId
而不必首先检索Student
实体。
Query query = em.createQuery("SELECT e FROM SemesterEnrollment e JOIN e.student s "
+ "WHERE s.studentId = :studentId");
这样可以避免委派和1+N问题,如果您真正需要检索的是SemesterEnrollment
实体,那么SemesterEnrollmentDAO
也应该这样做。
根据经验,DAO应该关注检索/持久化数据,因此任何业务逻辑或数据转换通常驻留在单独的业务层中。在您提供的示例中,可以询问如果对StudentDAO的调用因为根本没有数据而失败会发生什么。这是需要在服务层解决的问题。在您的示例中,可以将enrollment mentdao转换为enrollment mentservice。