以下是我认为的传统观点:
- 项目中的实体构成域模型。它们应该是可重用的,而不是与持久性技术紧密耦合(稍后我将讨论紧耦合与松耦合)
- 业务层,使用领域模型,但也暴露服务和其他东西。
- 数据访问层负责将域模型(实体)持久存储在持久存储中。
实体不应直接调用数据访问层。但是业务层将以某种方式加载和持久化领域模型的实体。
如果您将其映射到 Java EE 技术,您通常会得到如下信息:
- 实体 --> 带有 Hibernate/JPA 注释的 POJO。请注意,注释并不意味着与 JPA/Hibernate 紧密耦合,相同的 POJO 可以在没有 Hibernate 的其他地方使用。
- 业务层 --> Session EJB 或 Spring
- 数据访问层 --> JPA/Hibernate
这是一个粗略的草图,有很多可能的变体。您可以明显地跳过会话 EJB 并以另一种方式实现业务层。您还可以决定让业务层直接调用 JPA/Hibernate Session/EntityManager,在这种情况下 JPA/Hibernate 确实是 DAL,或者您可能希望将 Session/EntityManager 的访问包装到所谓的数据访问对象(DAO )。
关于 HQL,尽量坚持什么是可移植的,如果您使用原生 SQL,请遵循 SQL-92 约定。如果事情变得复杂,也许可以引入 DAO。这样,您就知道唯一存在 HQL 查询的地方就是 DAO。你也可以先在DAO中“程序化”实现查询逻辑,如果遇到性能问题,再用更复杂的HQL查询重新实现。
编辑
关于评论中的问题:
业务层依赖于数据层。如果您希望业务层不依赖于 Hibernate/JPA,那么您的数据层需要将Hibernate/JPA抽象出来。如果您将 DAO 用于数据层,情况就是如此。DAO 将是“Hibernate 上的薄手写持久层”(用你的话来说)。在您的案例中,我将为所有实体介绍 DAO。
您要问的是一个非常通用的设计问题。我无法为此给出明确的配方,也无法在一个答案中总结所有变体,因为这取决于具体情况。例如,到目前为止,我们还没有谈到事务问题,您通常从业务层开始,但数据层必须意识到这一点。这通常取决于所使用的技术和您的要求。
不过,这里是您可能感兴趣的资源列表:书籍Pattern of Enterprise Application Architecture、书籍Real World Java EE Patterns - Rethinking Best Practices、书籍Domain Driven Design以及更具体的模式Data Access Object、Repository pattern,Open Session in View(如果它用于 Web 应用程序),也许还有Anemic Domain Model。
编辑 2
好的,关于交易的更多句子:
事务在概念上应该在业务层进行管理;一个工作单元中需要做什么才能保持一致的定义确实取决于应用程序的逻辑。
使用 EJB3,可以使用注释和应用程序声明事务。服务器为您管理。有关更多信息,请参阅我的其他答案。使用 Spring,您还可以以声明方式标记事务,但我不知道细节。否则,您将需要自己启动/停止交易。无论您使用 JDBC 事务还是 JTA 事务,这都会略有不同。
事务还与 Hibernate/JPA 中的延迟加载有关。延迟加载的实体确实只有在存在当前事务时才能加载。如果事务在业务层中终止,则返回到表示层的实体需要预先加载。
To circumvent this problem, a popular pattern for web applications is Open Session in View, which I already mentioned. In this case, the presentation layer start/stop the transactions (which is slightly wrong conceptually), but works just fine with lazy loading.