以下是我认为的传统观点:
- 您项目中的实体形成了领域模型。它们应该是可重用的,不与持久化技术紧密耦合(稍后会回到紧 vs. 松耦合的问题)
- 业务层使用领域模型,但也公开服务和其他内容。
- 数据访问层负责将领域模型(实体)持久化到持久存储中。
实体不应直接调用数据访问层。但是业务层将以一种加载和持久化领域模型实体的方式进行调用。
如果将其映射到Java EE技术,通常会得到以下结果:
- 实体-->带有Hibernate/JPA注释的POJO。请注意,注释不意味着与JPA/Hibernate紧密耦合,同样的POJO可以在其他地方使用而不使用Hibernate。
- 业务层-->会话EJB或Spring
- 数据访问层--> JPA/Hibernate
这只是一个草图,有很多可能的变体。你可以跳过会话EJB并以另一种方式实现业务层。您还可以决定让业务层直接调用JPA/Hibernate Session/EntityManager,在这种情况下,JPA/Hibernate确实是数据访问层(DAL),或者您可能希望将访问Session/EntityManager封装在所谓的数据访问对象(DAO)中。
关于HQL,请尽量坚持可移植性,并且如果使用本地SQL,请遵循SQL-92约定。如果事情变得复杂,也许可以引入DAO。这样,您就知道仅在DAO中存在HQL查询的位置。您还可以先在DAO中“过程化”实现查询逻辑,如果遇到性能问题,则使用更复杂的HQL查询重新实现它。
编辑
关于您在评论中提出的问题:
业务层取决于数据层。如果要使业务层不依赖于Hibernate/JPA,则需要将数据层抽象掉Hibernate/JPA。如果您为数据层使用DAO,则情况就是如此。 DAO将是“手写的Hibernate上的薄持久层”(引用您的话)。我会为您的所有实体引入DAO。
你所问的是一个非常普遍的设计问题。我无法给出明确的解决方案,也不可能在一篇回答中总结所有变体,因为这取决于具体情况。例如,我们迄今为止还没有讨论过交易问题,这通常是从业务层开始的,但数据层必须知道。这通常取决于所使用的技术和您的要求。
以下是一些您可能感兴趣的资源列表:书籍《企业应用架构模式》, 书籍《实战Java EE设计模式-重新思考最佳实践》, 书籍《领域驱动设计》,更具体地涉及模式数据访问对象, 仓储模式, 在视图中打开会话(如果是网页应用程序),也许还有贫血领域模型。
编辑2
关于事务,再说几句:
事务的概念应该在业务层面上进行管理;在一个工作单元中需要做什么才能保持一致性的定义确实取决于应用程序的逻辑。
使用EJB3,可以通过注释声明事务,应用服务器会为您管理。有关更多信息,请参见我的另一个答案。使用Spring,您也可以以声明方式标记事务,但我不知道详细情况。否则,您将需要自己启动/停止事务。这将略有不同,无论您使用JDBC事务还是JTA事务。
事务还涉及Hibernate/JPA中的延迟加载。如果一个实体是延迟加载的,只有在当前事务存在时才能加载。如果在业务层面终止了事务,则返回到表示层的实体需要被急切地加载。
为了解决这个问题,Web应用程序中流行的模式是
在视图中打开会话,我已经提到过了。在这种情况下,表示层启动/停止事务(这在概念上略有错误),但与延迟加载完全兼容。