我过去已经构建了一些JPA内容,它们使用了每个DAO实例javax.persistence.EntityManager
的实例;这是大多数示例的设置方式。
public class BaseDaoThatEveryDaoExtends {
@PersistenceContext
private EntityManager entityManager;
}
我只是偶然发现使用javax.peristence.EntityManger
注释注入的静态 PersistenceContext
的代码,架构师告诉我这不会引起任何问题而且他们从来没有遇到任何问题即使在具有JTA和XA数据源的集群应用程序中:
public class BaseDaoThatEveryDaoExtends {
@PersistenceContext
private static EntityManager entityManager;
}
据我所知,这是一个反模式,因为EntityManager
包含一些状态信息并使其静态使整个状态应用程序变宽。这也使得课程很难测试。
这样做有其他缺点,还是使用EntityManager
的标准方式?
答案 0 :(得分:6)
我认为主要风险不是在EntityManager本身,而是在使用它时附加到实体管理器的应用程序上下文中。
假设您有两个不同的客户端向您的服务器发出请求,这两个客户端都调用了应用程序的两种不同方法,这两种方法都在不同的线程上运行,但都使用相同的实体管理器。
据我所知,实体经理将附加一个上下文,这个上下文将由两个客户共享。每次将实例加载到上下文时,线程都可以通过共享实体管理器使用。如果他们篡改对方的数据会怎么样?如果他们使用不同的事务隔离配置会发生什么?如何确定客户端1不会改变客户端2当前使用的数据?
如果其中一个客户端使上下文无效,另一个客户会怎么做?你如何以这种方式处理并发?
答案 1 :(得分:2)
EntityManager使用threadlocal保存其数据,因此可以保持对它的静态引用,因为访问它的所有线程都将被独立处理。实际上,如果EJB上下文以静态方式使用单例模式保存在EntityManager上,我不会感到惊讶。
就个人而言,我绝不会以静态方式定义它。这似乎是不必要的,最坏的情况可能会产生一些不可预见的副作用。
我能看到的一个问题是无意中从静态方法访问entityManager的能力:
public class BaseDaoThatEveryDaoExtends {
@PersistenceContext
private static EntityManager entityManager;
public static void doSomeStaticWork(){
...
entityManager.doSomething; //NPE possible!
}
}
我可以看到EntityManager没有被注入,在这种情况下导致NPE。
除此之外,使用EntityManager进行测试/模拟可能存在一些问题。
答案 2 :(得分:0)
EntityManagerFactory保证是线程安全的,所以我认为这是“正确的”方式: 在线程不安全的位置使用EMF并保护EntityManger本身免受线程问题的影响。