问题 如何在Java EE中建模?


让我们说,我决定使用Java EE堆栈来实现我的企业应用程序。

现在,对于域建模(或:用于设计MVC的M),我可以安全地假设和使用哪些API,以及我应该远离... ...通过一层抽象?

例如,

  1. 我应该继续使用Hibernate / JPA API调用我的模型吗?或者,我应该构建一个抽象...我自己的持久层,以避免对这两个特定的持久性API进行硬编码? 为什么我这样问: 几年前,有一个Kodo API被Hibernate取代。如果有人设计了一个持久层并对该层编码了模型的其余部分(而不是通过调用特定的供应商API来乱丢模型),那么它就可以(相对)轻松地从Kodo切换到Hibernate到xyz。

  2. 是否建议在域模型中积极使用持久性供应商提供的* QL?我不知道由于大量使用类似HQL的语言而产生的任何现实问题(如性能,可伸缩性,可移植性等)。 为什么我这样问: 我希望尽可能避免编写自定义代码,同时可以通过比SQL更便携的查询语言来完成。

对不起,但我是这个地区的新手。我在哪里可以找到关于此主题的更多信息?


11323
2018-04-14 06:33


起源



答案:


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

  • 项目中的实体构成域模型。它们应该是可重用的,而不是与持久性技术紧密耦合(稍后我会回来讨论紧耦合和松散耦合)
  • 业务层使用域模型,但也公开服务和其他东西。
  • 数据访问层负责将域模型(实体)持久存储在持久性存储中。

实体不应直接调用数据访问层。但是业务层将以加载和持久化域模型实体的方式进行。

如果将其映射到Java EE技术,通常会得到以下内容:

  • 实体 - >带有Hibernate / JPA注释的POJO。请注意,注释并不意味着与JPA / Hibernate紧密耦合,在没有Hibernate的情况下,可以在其他地方使用相同的POJO。
  • 业务层 - >会话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。

你问的是一个非常通用的设计问题。我不能给出明确的方法,也不可能在一个答案中总结所有变体,因为它取决于具体情况。例如,到目前为止,我们没有谈到交易问题,您通常从业务层开始,但数据层必须知道。这通常取决于所使用的技术和您的要求。

不过,这里列出了您可能感兴趣的资源:书籍 企业应用架构模式,这本书 真实世界Java EE模式 - 重新思考最佳实践,这本书 领域驱动设计 更具体地说是模式 数据访问对象存储库模式在视图中打开会话 (如果它是一个网络应用程序),也许 贫困领域模型

编辑2

好的,还有一些关于交易的句子:

交易应在概念上在业务层中进行管理;在一个工作单元中需要做的事情的定义是否一致,取决于应用程序的逻辑。

使用EJB3,可以使用注释和应用程序声明事务。服务器为您管理。看到 这个答案 我的更多信息。使用Spring,您还可以声明性地标记事务,但我不知道详细信息。否则,您需要自己开始/停止交易。无论您使用JDBC事务还是JTA事务,这都会略有不同。

事务还涉及Hibernate / JPA中的延迟加载。延迟加载的实体只有在存在当前事务时才能加载。如果事务在业务层中终止,则需要急切地加载返回到表示层的实体。

为了解决这个问题,Web应用程序的流行模式是 在视图中打开会话,我已经提到过。在这种情况下,表示层启动/停止事务(在概念上略有错误),但在延迟加载时工作得很好。


8
2018-04-14 07:39



我完全同意,并最终建议使用JPQL而不是HQL来坚持标准。 - snowflake
我这里的术语可能错了。当我说'域建模'时,我的意思是MVC三元组的M,它本质上是'核心应用程序'(包括业务层),甚至可以通过命令行界面驱动(通过重写V)和CLI的C)!现在,这个M将要做的事情之一是持久性。为此,M需要没有任何和所有特定于供应商的持久性东西。因此,需要一个超薄(或厚,复杂?)手写持久层而不是Hibernate。如果我在这里错了,请你纠正我。 - Harry
“如果事情变得复杂,可能会引入DAO。”你的意思是,根据具体情况(当然,假设DAO和HQL / JPQL和平共存)。我真的希望能够以这样的方式设计我的对象,HQL在很大程度上是足够的(由于关系范式的强大),对于真正复杂的查询,我编写了命令式代码。 - Harry
“这是一个粗略的草图,有很多可能的变种。”我在哪里可以找到有关这些变体的更多信息以及支持原理? - Harry
“例如,到目前为止,我们没有谈到交易问题,你通常从业务层开始,但数据层必须知道。这通常取决于所使用的技术和你的要求。”在我将此主题标记为已关闭之前,您/请/稍后再说一些关于此交易主题的句子。如果您提到的书籍/模式中涵盖了这个主题,那么请您说明......因为我首先要了解这件事情,而不是其他任何事情。到目前为止,感谢顺便说一下,即使你决定不回复(无论出于何种原因)。 - Harry


答案:


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

  • 项目中的实体构成域模型。它们应该是可重用的,而不是与持久性技术紧密耦合(稍后我会回来讨论紧耦合和松散耦合)
  • 业务层使用域模型,但也公开服务和其他东西。
  • 数据访问层负责将域模型(实体)持久存储在持久性存储中。

实体不应直接调用数据访问层。但是业务层将以加载和持久化域模型实体的方式进行。

如果将其映射到Java EE技术,通常会得到以下内容:

  • 实体 - >带有Hibernate / JPA注释的POJO。请注意,注释并不意味着与JPA / Hibernate紧密耦合,在没有Hibernate的情况下,可以在其他地方使用相同的POJO。
  • 业务层 - >会话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。

你问的是一个非常通用的设计问题。我不能给出明确的方法,也不可能在一个答案中总结所有变体,因为它取决于具体情况。例如,到目前为止,我们没有谈到交易问题,您通常从业务层开始,但数据层必须知道。这通常取决于所使用的技术和您的要求。

不过,这里列出了您可能感兴趣的资源:书籍 企业应用架构模式,这本书 真实世界Java EE模式 - 重新思考最佳实践,这本书 领域驱动设计 更具体地说是模式 数据访问对象存储库模式在视图中打开会话 (如果它是一个网络应用程序),也许 贫困领域模型

编辑2

好的,还有一些关于交易的句子:

交易应在概念上在业务层中进行管理;在一个工作单元中需要做的事情的定义是否一致,取决于应用程序的逻辑。

使用EJB3,可以使用注释和应用程序声明事务。服务器为您管理。看到 这个答案 我的更多信息。使用Spring,您还可以声明性地标记事务,但我不知道详细信息。否则,您需要自己开始/停止交易。无论您使用JDBC事务还是JTA事务,这都会略有不同。

事务还涉及Hibernate / JPA中的延迟加载。延迟加载的实体只有在存在当前事务时才能加载。如果事务在业务层中终止,则需要急切地加载返回到表示层的实体。

为了解决这个问题,Web应用程序的流行模式是 在视图中打开会话,我已经提到过。在这种情况下,表示层启动/停止事务(在概念上略有错误),但在延迟加载时工作得很好。


8
2018-04-14 07:39



我完全同意,并最终建议使用JPQL而不是HQL来坚持标准。 - snowflake
我这里的术语可能错了。当我说'域建模'时,我的意思是MVC三元组的M,它本质上是'核心应用程序'(包括业务层),甚至可以通过命令行界面驱动(通过重写V)和CLI的C)!现在,这个M将要做的事情之一是持久性。为此,M需要没有任何和所有特定于供应商的持久性东西。因此,需要一个超薄(或厚,复杂?)手写持久层而不是Hibernate。如果我在这里错了,请你纠正我。 - Harry
“如果事情变得复杂,可能会引入DAO。”你的意思是,根据具体情况(当然,假设DAO和HQL / JPQL和平共存)。我真的希望能够以这样的方式设计我的对象,HQL在很大程度上是足够的(由于关系范式的强大),对于真正复杂的查询,我编写了命令式代码。 - Harry
“这是一个粗略的草图,有很多可能的变种。”我在哪里可以找到有关这些变体的更多信息以及支持原理? - Harry
“例如,到目前为止,我们没有谈到交易问题,你通常从业务层开始,但数据层必须知道。这通常取决于所使用的技术和你的要求。”在我将此主题标记为已关闭之前,您/请/稍后再说一些关于此交易主题的句子。如果您提到的书籍/模式中涵盖了这个主题,那么请您说明......因为我首先要了解这件事情,而不是其他任何事情。到目前为止,感谢顺便说一下,即使你决定不回复(无论出于何种原因)。 - Harry


您的域模型及其持久层理论上应该是分开的 - 不需要调用的类 Entity 要知道是否以及如何持久化,所以你可以使用像Hibernate这样的东西来创建持久层而不会污染域模型类本身。您没有“针对该层对[...]模型进行编码” - 您对模型进行编码,然后将其映射到具有某种ORM层的持久性存储,其中域模型不依赖于ORM层。显然,持久层将取决于域模型,但这很好。

因为你问的原因,我个人害怕使用(N)Hibernate过多的HQL,但有时候它是不可避免的。你已经知道并且自己强调了那里的主要问题,所以你不可能过度使用它。


4
2018-04-14 06:40



“然后将它映射到具有某种ORM层的持久存储”:是的,我只是意味着它。看起来你的意思是:在Hibernate之上构建一个层,而不是乱丢直接调用Hibernate API调用的模型,对吧? - Harry
关闭但不完全,我说要构建一个模型,然后将它连接到持久层。当您对域模型类进行编码时,您并不关心他们是如何进入数据库的。您当然不应该觉得自己正在“在Hibernate之上”构建这些类。 - David M