6

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

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

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

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

更好的解决方案是什么?

4

3 回答 3

5

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

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

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

例如

@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-38https://issues.jboss.org/ browse/AS7-5549),因此请测试这是否适用于您的服务器。

于 2012-12-07T22:03:28.123 回答
2

容器管理的实体管理器与当前 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) {
        ...
    }
    ...
}
于 2012-12-04T09:44:57.073 回答
1

复合持久性单元 - Java EE

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

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

介绍

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

复合持久性单元的工作

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

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

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

persistence.xml 中的设置

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

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

)。

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

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

)。@Entity此处包括存储有关 Web 事务、登录、会话等数据的其他实体(用.

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

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

复合持久性单元

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

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

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

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

在复合持久化单元的 XML-definition 中,需要设置以下属性

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

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

Java中持久化单元的使用

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

@Stateless
public class DataLayer {

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

现在可以对包含在复合实体成员之一中的每个实体执行正常的“em”操作persist,例如find和。merge

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

马文

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

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

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

陷阱:

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

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

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

于 2017-05-04T15:02:25.997 回答