在Java EE应用程序中处理多个EntityManager

我有大约10个EntityManagers的Java EE应用程序(EM的数量可能会增加)。 我的应用程序还包含许多无状态,有状态和消息驱动的bean。

我可能会将所有内容存储在一个单独的bean中,并使用其他bean访问它,而不是在每个bean中使用@PersistenceContext注入我的EM(以及两个方法来检测用于用户的EM)。 像那样,不用担心可维护性。

然而,将EM存储在一个单独的bean中是否是线程安全的? 瓶颈会出现吗?

另一个解决方案是创建一个抽象类,所有bean都将扩展它。

什么是更好的解决方案?

容器管理实体管理器使用当前JTA事务自动传播,并且映射到同一持久性单元的EntityManager引用提供对该事务中持久性上下文的访问。 因此,除了并发问题之外,从单例共享实体管理器并不是一个好习惯,它会导致为您在bean上调用的每个方法使用相同的事务上下文。
一个简单的解决方案是在bean中注入EntityManagerFactory引用并创建调用createEntityManager()方法的EntityManager对象。 缺点是您应该手动管理事务,不再依赖容器。
否则,另一种方法可能是将所有实体管理器注入主企业bean,并在服务bean中实现业务逻辑,并使用适当的管理器传递方法。 后一种解决方案的一个例子:

 @Stateless class MainBean { @PersistenceContext EntityManager em1; @PersistenceContext EntityManager em2; ... @EJB WorkerBean1 workerBean1; @EJB WorkerBean2 workerBean2; ... void method1(Object param1, Object param2) { workerBean1.method1(em1, param1, param2); } void method2(Object param1, Object param2, Object param3) { workerBean2.method2(em2, param1, param2, param3); } ... } @Stateless class WorkerBean1 { void method1(EntityManager em, Object param1, Object param2) { ... } ... } @Stateless class WorkerBean2 { void method2(EntityManager em, Object param1, Object param2, Object param3) { ... } ... } 

实体管理器不应该是线程安全的,因此您不应该通过Singleton共享它们。 这与为什么不应该将实体管理器注入Servlet的原因相同,以及为什么在这样的Web组件中从JNDI进行查找 – 应该随时返回实体管理器的不同实例。

在实践中,一些实现可以提供线程安全的实体管理器,因此在测试期间它似乎可行。 但是,为了便于携带并保护您免受升级困难,您永远不应该依赖于此。

您可以在一个bean中定义所有实体管理器,而不是从公共基类inheritance,并在需要实体管理器的任何地方注入它。

例如

 @Stateless public class EntityManagerProviderBean { @PersistenceContext(unitName="foo") private EntityManager entityManagerFoo; @PersistenceContext(unitName="bar") private EntityManager entityManagerBar; public EntityManager getEntityManager() { return ...? entityManagerFoo : entityManagerBar; } } 

(其中……是用于选择正确的实体管理器的逻辑)

将此注入需要实体管理器的bean:

 @Stateless public class MyService { @EJB private EntityManagerProviderBean entityManagerProvider; public void doStuff(MyEntity myEntity) { entityManagerProvider.getEntityManager().update(myEntity); } } 

或者,以下可能更整洁:

 @Stateless @PersistenceContexts({ @PersistenceContext(unitName="foo", name = "fooENC"), @PersistenceContext(unitName="bar", name = "barENC") } ) public class EntityManagerProviderBean { @Resource private EJBContext context; public EntityManager getEntityManager() { return (EntityManager) context.lookup(... ? "fooENC" : "barENC"); } } 

最后一个示例将所有持久化上下文映射到bean的ENC中,在那里可以方便地以编程方式检索它们。

不幸的是,人们忘记将后一种语法的测试添加到TCK,随后主要供应商忘记实现它(请参阅http://java.net/jira/browse/JPA_SPEC-38和https://issues.jboss.org/浏览/ AS7-5549 ),所以测试它是否适用于您的服务器。

复合持久性单元 – Java EE

在Java EE中处理多个实体管理器(即多个持久性单元)的方法是使用复合持久性单元(CPU)。 可以从EE网络应用程序中的一个单点(数据层)评估这种复合持久性单元。 这需要是一个@Stateless EE bean,以便使用@PersistenceContext

已经引入了复合持久性单元,以便在各种Java应用程序中重用实体类。 CPU是企业架构的一项function。 我选择使用EclipseLink作为展示,因为我从正在运行的生产应用程序中获得了积极的经验。

介绍

在某些情况下,实体包含服务器环境中更多Web服务所需的通用数据。 例如,一般的“名称 – 地址”实体,“用户 – 密码 – 角色”实体,“文档 – 关键字 – 索引”实体等。复合持久性单元实现有助于指定每个实体定义的来源。只有一个地方(’单点定义’)。 随后,这些实体定义可以包含在需要此实体访问的每个Java Web应用程序中。

复合持久化单元的工作

以下教程说明了复合持久性单元的工作: EclipseLink复合持久性单元

复合持久性单元的概念首先定义成员持久性单元。 每个成员持久性单元可以与不同的数据库相关联,但是成员持久性单元也可以都引用相同的实际数据库。 我有后者的经验,其中EclipseLink(版本2.6.4)与一个Postgress数据库结合使用。

需要Maven来实现所需的模块化方法。

persistence.xml中的设置

复合持久性单元成员定义如下:在专用Maven模块中逐个编程一组相关实体(Java @Entity类)。 在这个Maven模块中定义一个复合持久性单元成员(很重要!)。 复合单元成员PuPersonData指的是表征人数据的这组相关实体。 将成员持久性单元PuPersonData定义为(

  ... jdbc/PostgresDs ... 

)。

在第二个Maven模块中,定义另一个复合持久性单元成员PuWebTraffic(

  ... jdbc/PostgresDs ... 

)。 这里包括其他实体(用@Entity表示的Java类),用于存储有关Web事务,登录,会话等的数据。无需说明,两个复合持久性单元成员必须相对于实体不相交,实体中不允许重叠-names。

两个持久性单元成员都在其XML定义中具有以下属性:

   ...  

复合持久单元

我们现在在第三个Maven模块中定义复合持久性单元CPuPersonSessionData,其包括持久性单元成员PuPersonData和PuWebTraffic。

  

该复合持久性单元CPuPersonSessionData通过包括两个相关Maven模块的编译所产生的jar来引用两个持久性单元成员PuPersonData和PuWebTraffic。

 ... PuPersonData.jar PuWebTraffic.jar ... 

在复合持久性单元的XML定义中,需要设置以下属性

   ...  

此设置可确保Java EE对复合持久性单元的处理方式与其持久性单元成员的处理方式不同。

在Java中使用持久性单元

在将使用人员数据和流量数据存储和检索实体的Java Web应用程序中,仅包含复合持久性单元

 @Stateless public class DataLayer { @PersistenceUnit(unitName="CPuPersonSessionData") EntityManager em; ... 

现在可以对包含在其中一个复合实体成员中的每个实体执行正常的’em’操作,例如persistfindmerge

在Payara下,此复合持久性单元不需要XA事务来处理与每个持久性单元成员相关的实体。

Maven的

Maven POM文件需要包含相关模块的规范。

 ...  PersonData WebTraffic PersonSessionData  ... 

每个模块的POM文件需要配置为普通的Maven项目,引用父POM文件。

陷阱:

  • 您需要正确配置Maven多模块项目,这可能有点棘手。 每个复合持久性单元成员构成单独的Maven模块。 复合持久性单元也是一个单独的Maven模块。 需要首先在Maven序列中编译成员。
  • 在编译复合持久性单元的模块时,需要找到复合持久性单元中的“jar”。
  • 每个复合持久性单元成员的实体需要在生成的“jar”中直接在“classes”目录中可用(通过Maven添加实体的额外路径,这是可能的,但很复杂)。
  • 持久性单元成员的“jar”需要在“classes”目录中可用,以便复合持久性单元找到它们。

获得的好处是一个整洁的企业数据层,它与可重用实体一起工作,每个实体都有一个中心定义。 此外,可以执行跨单元本机SQL查询。 我也让这个工作了。

文档指出,当复合持久性单元成员在不同的实际数据库上运行时,跨单元本机查询将不起作用。 这仍应该得到validation。