正在解决的问题
Servlet 和 JSP 必须是无状态的,因为它们在多个线程之间共享。AnEntityManager
确实保持状态,因此并发线程不能共享单个实例。
我们想要一个平滑/无缝的机制来获取 EntityManager,最好由 Servlet 容器管理。
Servlet-容器托管持久化上下文
让我们ContainerManagedPersistenceContext
在 Servlet/JSP 运行时中引入一个。
我们稍后会定义它。让我们首先看一下如何使用它来注入EntityManager
in JSP
:
<%! @Inject
@ContainerManagedPersistenceContext.Qualifier
public EntityManager em;
%>
或者,更好的是进入控制器(因为我们确实想将数据恢复/业务逻辑从我们的 JSP 中分离出来,对吧?):
@Named
@SessionScoped
public class SessionController implements Serializable
{
...
@Inject
@ContainerManagedPersistenceContext.Qualifier
private EntityManager em;
}
但我(还)没有可用的 CDI
如果您没有 CDI,但您有 JSF,则可以将上下文作为旧式标准 JSF 注入@ManagedProperty
:
@Named
@SessionScoped
public class SessionController implements Serializable
{
...
@ManagedProperty(value = "#{containerManagedPersistenceContext}")
ContainerManagedPersistenceContext cmpContext;
...
public void myMethod() {
EntityManager em = cmpContext.getEntityManager();
try {
...
} finally {
em.close();
}
}
}
请记住——出于同样的原因,我们必须首先进行这项工作——EntityManager
绝不能在任何地方缓存/保存。
交易
使用for begin/commit/rollbackEntityTransaction
提供的:EntityManager
EntityTransaction 交易 = em.getTransaction();
ContainerManagedPersistenceContext
这被定义为一个应用程序范围的控制器和一个PersistenceContext
:
@PersistenceContext(name = ContainerManagedPersistenceContext.NAME,
unitName = ContainerManagedPersistenceContext.UNIT_NAME)
@ApplicationScoped
public class ContainerManagedPersistenceContext implements Serializable
{
private static final long serialVersionUID = 1L;
// UNITNAME must match persistence.xml: <persistence-unit name="myUnitName">
public static final String UNITNAME = "myUnitName";
public static final String NAME = "persistence/" + UNIT_NAME;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD,
ElementType.PARAMETER, ElementType.TYPE})
public static @interface Qualifier { }
// Servlets must be stateless (shared across multiple threads).
// EntityManager is not stateless (cannot be shared across threads).
// Obtain Container Managed EntityManager - and do NOT cache.
@Produces @Qualifier
public static EntityManager getEntityManager() throws NamingException
{
EntityManager lookup = InitialContext.doLookup("java:comp/env/" + NAME);
return lookup;
}
}
限制
如所写,这为 Servlet 容器定义了一个专门命名的 PersistenceContext。由于unitName
未参数化,因此它不提供以下灵活性级别:
@PersistenceContext(unitName = "unitname")
public EntityManager em;
备择方案
在 Servlet 上定义 PersistenceContext,并使用 JNDI 名称查找。