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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13699201/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 13:41:44  来源:igfitidea点击:

Handle multiple EntityManager in Java EE application

javajpaentitymanagerconcept

提问by Olivier J.

I have Java EE application with about 10 EntityManagers (number of EMs will probably increase). My application also contains many stateless, statefull and message driven beans.

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

Rather than inject in each bean my EMs with @PersistenceContext(and 2 methods to detect which EM to use for user), I probably store all of that inside a singleton bean and access it with others beans. Like that, no worries about maintainability.

与其将我的 EM 注入每个 bean @PersistenceContext(以及 2 种检测用户使用哪个 EM 的方法),我可能会将所有这些都存储在一个单例 bean 中,并与其他 bean 一起访问它。这样,不用担心可维护性。

Nevertheless, is it thread-safe to store EMs inside one singleton bean? Can a bottleneck appear?

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

Another solution is to create an abstract class and all beans will extend it.

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

What is the better solution?

什么是更好的解决方案?

采纳答案by remigio

Container managed entity managers are automatically propagated with the current JTA transaction and EntityManagerreferences that are mapped to the same persistence unit provide access to the persistence context within that transaction. So it's not good practice to share an entity manager from a singleton, apart from concurrency problems, it would result in using the same transaction context for every method you call on your beans.
A simple solution to your need is to inject EntityManagerFactoryreferences in your beans and create EntityManagerobjects calling the createEntityManager()method. The drawback is that you should manage transactions manually, no more relying on the container.
Otherwise another approach could be inject all of your entity managers in a main enterprise bean and implement business logic in service beans with methods to which you pass the appropriate managers. An example of the latter solution:

容器管理的实体管理器与当前 JTA 事务一起自动传播EntityManager,并且映射到同一持久性单元的引用提供对该事务内的持久性上下文的访问。因此,除了并发问题之外,从单例共享实体管理器并不是一个好习惯,这会导致对您在 bean 上调用的每个方法使用相同的事务上下文。
满足您需求的一个简单解决方案是EntityManagerFactory在 bean 中注入引用并创建EntityManager调用该createEntityManager()方法的对象。缺点是您应该手动管理事务,不再依赖容器。
否则,另一种方法可能是将所有实体管理器注入主企业 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) {
        ...
    }
    ...
}

回答by Arjan Tijms

An entity manager is not supposed to be thread-safe, so you shouldn't share ones via a Singleton. It's the same reason as why you should not inject an entity manager into a Servlet, and why a lookup from JNDI in such a web component -should- return a different instance of the entity manager ever time.

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

In practice some implementations may provide an entity manager that is thread-safe, so during testing it may seem to work. However, for the sake of portability and to protect you against upgrade woes, you should never rely on this.

在实践中,一些实现可能会提供一个线程安全的实体管理器,因此在测试期间它似乎可以工作。但是,为了便携性和保护您免受升级问题的影响,您永远不应依赖于此。

Instead of inheriting from a common base class, you could define all your entity managers in one bean, and inject that wherever you need an entity manager.

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

E.g.

例如

@Stateless
public class EntityManagerProviderBean {

    @PersistenceContext(unitName="foo")
    private EntityManager entityManagerFoo;

    @PersistenceContext(unitName="bar")
    private EntityManager entityManagerBar;

    public EntityManager getEntityManager() {
        return ...? entityManagerFoo : entityManagerBar;
    }
}

(where ... is the logic you use to select the right entity manager)

(其中 ... 是您用来选择正确实体管理器的逻辑)

Inject this into a bean needing an entity manager:

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

@Stateless
public class MyService {

    @EJB
    private EntityManagerProviderBean entityManagerProvider;

    public void doStuff(MyEntity myEntity) {
        entityManagerProvider.getEntityManager().update(myEntity);
    }

}

Alternatively the following would perhaps be even neater:

或者,以下内容可能会更简洁:

@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");
    }
}

The last example maps all persistence contexts into the ENC of the bean, where they can be conveniently retrieved programmatically.

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

Unfortunately, people forgot to add tests for the latter syntax to the TCK and subsequently major vendors forgot to implement it (see http://java.net/jira/browse/JPA_SPEC-38and https://issues.jboss.org/browse/AS7-5549), so test if this works on your server.

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

回答by Flying Dutchman

Composite persistence units - Java EE

复合持久性单元 - Java EE

The way to handle multiple entity managers, i.e. multiple persistence units, in Java EE is to use composite persistence units (CPUs). Such a composite persistence unit can be assessed from one single point in the EE web-application, a datalayer. This needs to be a @StatelessEE bean though in order to work with the @PersistenceContext.

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

Composite persistence units have been introduced to make possible reusing entity classes, among various Java applications. CPUs are a feature of Enterprise architecture. I choose to use EclipseLink as showcase, as I have positive experience with that from a running production application.

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

Introduction

介绍

In some cases, entities contain general data that is needed across more web-services in a server landscape. Take for example a general ‘name-address' entity, a ‘user-password-role' entity, a ‘document-keyword-index' entity, etc. A composite persistence unit implementation facilitates that the source of each entity definition is specified in only one place (‘single point of definition'). These entity definitions can subsequently be included in each Java web-application that needs this entity access.

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

Working of composite persistence unit

复合持久化单元的工作

The working of a composite persistence unit is illustrated by the following tutorial: EclipseLink composite persistence units

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

The concept of composite persistence units works by first defining member persistence units. Each member persistence unit may be associated with a different database, but the member persistence units can also all refer to the same actual database. I have experience with the latter, where EclipseLink (version 2.6.4) was used in combination with one Postgress database.

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

Maven is needed to make possible the required modular approach.

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

Settings in persistence.xml

persistence.xml 中的设置

A composite persistence unit member is defined as follows: Program a group of related entities (Java @Entityclasses), one-by-one, in a dedicated Maven module. Define in this Maven module also a composite persistence unit member (important!). The composite unit member PuPersonDatarefers to this set of related entities that characterizes person data. Define the member persistence unit PuPersonData as (

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

<persistence-unit name="PuPersonData" transaction-type="JTA">
...
    <jta-data-source>jdbc/PostgresDs</jta-data-source>
...

).

)。

In a second Maven module, define another composite persistence unit member, PuWebTraffic (

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

<persistence-unit name="PuWebTraffic" transaction-type="JTA">
...
    <jta-data-source>jdbc/PostgresDs</jta-data-source>
...

). Include here other entities (Java classes denoted with @Entity) that store data about web-transactions, logon, sessions, etc. Needless to state, the two composite persistence unit members must be disjoint with respect to entities, no overlap is allowed in entity-names.

)。这里包括其他实体(用 表示的 Java 类@Entity),它们存储有关 Web 事务、登录、会话等的数据。不用说,两个复合持久性单元成员在实体方面必须是不相交的,实体名称中不允许重叠.

Both persistence unit members have in their XML-definitions the property:

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

<properties>
    <property name="eclipselink.composite-unit.member" value="true"/>
    ...
</properties>

Composite persistence unit

复合持久化单元

We now define in a third Maven module the composite persistence unit CPuPersonSessionData that includes both the persistence units members PuPersonData and PuWebTraffic.

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

<persistence-unit name="CPuPersonSessionData" transaction-type="JTA">

This composite persistence unit CPuPersonSessionData refers to the two persistence unit members, PuPersonData and PuWebTraffic, by means of including the jars that result from compilation of the two pertaining Maven modules.

这个复合持久化单元CPuPersonSessionData 引用了两个持久化单元成员PuPersonData 和PuWebTraffic,通过包含两个相关Maven 模块编译产生的jar。

...
    <jar-file>PuPersonData.jar</jar-file>
    <jar-file>PuWebTraffic.jar</jar-file>
...

In the XML-definition of the composite persistence unit, the following property needs to be set

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

<properties>
    <property name="eclipselink.composite-unit" value="true"/>
    ...
</properties>

This setting ensures that the composite persistence unit is treated differently by Java EE than its persistence unit members.

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

Use of persistence unit in Java

Java中持久化单元的使用

In the Java web-application that is going to store and retrieve entities with both person-data and traffic-data, only the composite persistence unit is included

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

@Stateless
public class DataLayer {

    @PersistenceUnit(unitName="CPuPersonSessionData")
    EntityManager em; 
    ...

The normal 'em' operations such as persist, findand mergecan now be performed on each entity, contained in one of the composite entity members.

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

Under Payara, no XA-transactions were needed for this composite persistence unit to address the entities pertaining to each of the persistence unit members.

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

Maven

马文

The Maven parentPOM file needs to contain the specifications for the pertaining modules.

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

...
    <modules>
            <module>PersonData</module>
            <module>WebTraffic</module>
            <module>PersonSessionData</module>
    </modules>
...

The POM-file of each module needs to be configured as a normal Maven-project, referring to the parent POM-file.

每个模块的POM-file需要配置为一个普通的Maven-project,参考父POM-file。

Pitfalls:

陷阱:

  • You need to configure the Maven multi-module project correctly, which can be somewhat tricky. Each composite persistence unit member constitutes a separate Maven module. Also the composite persistence unit is a separate Maven module. The members need to be compiled first, in Maven sequence.
  • The ‘jars' in the composite persistence unit need to be found when compiling the module of the composite persistence unit.
  • The entities of each composite persistence unit member need to be available in the resulting ‘jar', directly in the ‘classes' directory (adding extra paths to the entities, via Maven, is possible but complex).
  • The ‘jars' of the persistence unit members need to be available in the ‘classes' directory for the composite persistence unit to find them.
  • 您需要正确配置 Maven 多模块项目,这可能有些棘手。每个复合持久性单元成员构成一个单独的 Maven 模块。此外,复合持久性单元是一个单独的 Maven 模块。需要先编译成员,按照 Maven 顺序。
  • 编译复合持久单元的模块时需要找到复合持久单元中的'jars'。
  • 每个复合持久性单元成员的实体需要在生成的“jar”中可用,直接在“classes”目录中(通过 Maven 添加实体的额外路径是可能的,但很复杂)。
  • 持久性单元成员的“jars”需要在“classes”目录中可用,以便复合持久性单元找到它们。

The benefit gained is a neat Enterprise data-layer that works with reusable entities, each with one central definition. Moreover, it is possible to perform cross-unit native SQL-queries. I got this to work also.

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

Documentation states that cross-unit native queries will not work when the composite persistence unit members run on different, actual databases. This should still be verified.

文档指出,当复合持久性单元成员在不同的实际数据库上运行时,跨单元本机查询将不起作用。这个还是要验证的。