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

Oli*_* J. 5 java jpa entitymanager concept

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

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

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

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

什么是更好的解决方案?

Arj*_*jms 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

@Stateless
public class MyService {

    @EJB
    private EntityManagerProviderBean entityManagerProvider;

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

}
Run Code Online (Sandbox Code Playgroud)

或者,以下可能更整洁:

@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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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


rem*_*gio 2

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

@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) {
        ...
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)