如何在Java EE中进行建模?

11
假设我决定使用Java EE堆栈来开发企业应用程序。现在,为了域建模(或:为了设计MVC中的M),我可以安全地假定并使用哪些API,以及哪些API应该避免使用......比如通过一层抽象?
例如:
1. 我应该继续在我的Model中添加对Hibernate/JPA API的调用吗?还是应该构建自己的抽象......一个持久化层,以避免针对这两个特定持久化API进行硬编码?为什么我会这样问:几年前,有一个Kodo API被Hibernate取代。如果一个人设计了一个持久化层,并针对这个层编写了其余的Model(而不是在Model中添加对特定供应商API的调用),那么就可以相对轻松地从Kodo切换到Hibernate或xyz。
2. 在您的领域模型中积极利用持久性供应商提供的*QL是否推荐?我不知道任何真实世界中的问题(如性能、可扩展性、可移植性等)是否出现在类似于HQL的语言的大量使用中。为什么我会这样问:我想尽可能地避免编写自定义代码,当可以通过比SQL更便携的查询语言完成相同的任务时。
对不起,但我是这个领域的完全新手。我可以在哪里找到更多关于这个主题的信息?
2个回答

8

以下是我认为的传统观点:

  • 您项目中的实体形成了领域模型。它们应该是可重用的,不与持久化技术紧密耦合(稍后会回到紧 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应用程序中流行的模式是在视图中打开会话,我已经提到过了。在这种情况下,表示层启动/停止事务(这在概念上略有错误),但与延迟加载完全兼容。

2
我完全同意,并最终建议使用JPQL而不是HQL,以遵循标准。 - snowflake
我在术语上可能有错误。当我说“领域建模”时,我的意思是MVC三部分中的M,本质上是“核心应用程序”(包括业务层),您甚至可以通过重写CLI的V和C来驱动它!现在,这个M要做的事情之一就是持久性。为此,M需要摆脱任何特定于供应商的持久性功能。因此,需要一个薄的(或厚的/复杂的?)手写持久性层来覆盖Hibernate。如果我说错了,请指正我。 - Harry
如果情况变得复杂,或许可以引入DAO。这意味着需要根据具体情况考虑(当然,前提是DAO和HQL/JPQL能够和平共处)。我真的很希望能够以这种方式设计我的对象,使得大多数情况下只需要使用HQL就足够了(由于关系范式的强大),而对于真正复杂的查询,则需要编写命令式代码。 - Harry
"这只是一个草图,有很多可能的变体。我在哪里可以找到更多关于这些变体以及支持理由的信息?" - Harry
例如,我们迄今为止还没有讨论过事务的问题,这通常是在业务层开始的,但数据层必须意识到这一点。这通常取决于所使用的技术和您的要求。在我关闭此线程之前,您能否就事务的主题再说几句话?如果您提到的书籍/模式已经涵盖了这个主题,您能否请说明一下...因为我非常渴望首先学习这个东西。感谢迄今为止的帮助,即使您决定不回复(出于任何原因)。 - Harry
还想了解更多信息 :-) 但与此同时,我会关闭这个主题。我认为你已经给了我足够的东西来咬/嚼/消化...在接下来的几天里。+1。 - Harry

4

理论上,您的域模型及其持久化层应该是分离的 - 没有必要让名为Entity的类知道它是否以及如何持久化,因此您可以使用类似于Hibernate的东西来创建持久化层,而不会污染域模型类本身。您不需要“针对此层编写[...]模型” - 您编写模型,然后使用某种ORM层将其映射到持久存储中,在这里域模型不依赖于ORM层。显然,持久化层将依赖于域模型,但这没关系。

出于您所提出的原因,我个人避免过多地使用(N)Hibernate中的HQL,但有时无法避免。您已经知道并且已经强调了主要问题,因此您不太可能过度使用它。


然后使用某种ORM层将其映射到持久存储中。是的,我指的就是这个。看起来你的意思是:在Hibernate之上构建一个层,而不是在模型中直接调用Hibernate API,对吗? - Harry
1
接近但不完全相同,我的意思是先构建一个模型,然后将其连接到持久层。当您编写域模型类时,您不需要关心它们是否进入数据库,甚至不需要知道如何进入。您肯定不应该觉得自己正在构建那些类“在Hibernate之上”。 - David M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接