使用 Extended PersistentContext 时实体是非托管的
Entities are unmanaged when using Extended PersistentContext
环境:
JDK 1.8
WildFly 10.0.0.Final
我有以下@Stateful bean
@Stateful
@SessionScoped
@Local(CdiStatefulEmployeeService.class)
public class CdiStatefulEmployeeBean implements CdiStatefulEmployeeService {
@PersistenceContext(name = "employees", type = PersistenceContextType.EXTENDED)
EntityManager extendedEm;
private Employee cached;
@Override
public String service() {
cached = extendedEm.find(Employee.class, 499983);
return cached.getFirstName();
}
@Override
public String updateEntity() {
cached.setFirstName("Uri2");
//extendedEm.flush(); -- Line 1
return cached.getFirstName();
}
}
和以下 Servlet 客户端
@WebServlet("/atInjectedStatefulEjbClient")
public class AtInjectedStatefulEjbClient extends HttpServlet {
@Inject
CdiStatefulEmployeeService statefulBean;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession(true);
resp.setContentType("text/plain");
PrintWriter pw = resp.getWriter();
pw.println(statefulBean.service());
pw.println();
pw.println(statefulBean.updateEntity());
session.invalidate();
pw.flush();
pw.close();
}
}
观察:调用 bean.updateEntity() 方法不会自动保存更改,即将名字设置为 "Uri2"。
问题:在扩展持久性上下文的情况下,是否跨调用管理实体?
调用 flush()(即取消注释第 1 行)也没有任何效果。基本上实体不在 updateEntity() 调用中管理。我觉得这很奇怪。有什么想法吗?
更新:
只是为了排除这种可能性,尝试使用相同的代码但使用以下代码
- 没有@SessionScoped
- 无@Inject(替换为 servlet doGet() 中的 JDNI 查找)
- 在bean中添加了@Remove方法
- 让 servlet doGet() 在调用其他 2 个方法后调用 @Remove
结果:同样的问题。实体在第一次服务调用后未被管理
拉凯什
假设 service
方法在 updateEntity
显示描述的行为之前被调用:service
方法的事务属性是默认的,这意味着,正如您在评论中指出的 updateEntity
,事务在 service
调用结束时提交。此提交使 cached
实体不受管理。我建议用 @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
注释 service
方法,它应该保持 cached
实体被管理,随后调用 updateEntity
找到被管理的实体。
通常,对于扩展的持久性上下文,外观 EJB 类 被注释为 @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
并且只有应该提交的 EJB 方法才会获得不同的事务属性(参见例如文章 Domain-driven design with Java EE 6) .
永远不要将 STATEFUL(依赖于会话)bean 注入 servlet(由许多用户使用 = 由许多会话使用)。如果您使用 CMT,事务的提交将在调用 @Remove 标记的方法之后执行。它在哪里?它可以是空的,但它对有状态的 bean 很重要。
但这将是一个可行的解决方案:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form>
<h:inputText value="#{employeeMB.emplyeeName}"/>
<h:commandButton value="Submit" action="#{employeeMB.onSubmit}"/>
</h:form>
</h:body>
</html>
@ManagedBean
@SessionScoped
@Getter
@Setter
public class EmployeeMB
{
private String employeeName;
@EJB
private EmployeeSB employeeSB;
public void onSubmit()
{
employeeSB.persist( employeeName );
employeeName = "";
}
}
@Stateless
@LocalBean
public class EmployeeSB
{
@PersistenceContext
private EntityManager em;
public void persist( String employeeName_ )
{
Employee e = new Employee();
e.setName( employeeName_ );
em.persist( e );
}
}
似乎是 WildFly 中的错误。所有文档(规范、书籍等)都指出扩展上下文旨在防止实体在事务结束时分离。
已归档的 Wildfly 问题:https://issues.jboss.org/browse/WFLY-6383
环境:
JDK 1.8
WildFly 10.0.0.Final
我有以下@Stateful bean
@Stateful
@SessionScoped
@Local(CdiStatefulEmployeeService.class)
public class CdiStatefulEmployeeBean implements CdiStatefulEmployeeService {
@PersistenceContext(name = "employees", type = PersistenceContextType.EXTENDED)
EntityManager extendedEm;
private Employee cached;
@Override
public String service() {
cached = extendedEm.find(Employee.class, 499983);
return cached.getFirstName();
}
@Override
public String updateEntity() {
cached.setFirstName("Uri2");
//extendedEm.flush(); -- Line 1
return cached.getFirstName();
}
}
和以下 Servlet 客户端
@WebServlet("/atInjectedStatefulEjbClient")
public class AtInjectedStatefulEjbClient extends HttpServlet {
@Inject
CdiStatefulEmployeeService statefulBean;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession(true);
resp.setContentType("text/plain");
PrintWriter pw = resp.getWriter();
pw.println(statefulBean.service());
pw.println();
pw.println(statefulBean.updateEntity());
session.invalidate();
pw.flush();
pw.close();
}
}
观察:调用 bean.updateEntity() 方法不会自动保存更改,即将名字设置为 "Uri2"。
问题:在扩展持久性上下文的情况下,是否跨调用管理实体?
调用 flush()(即取消注释第 1 行)也没有任何效果。基本上实体不在 updateEntity() 调用中管理。我觉得这很奇怪。有什么想法吗?
更新:
只是为了排除这种可能性,尝试使用相同的代码但使用以下代码
- 没有@SessionScoped
- 无@Inject(替换为 servlet doGet() 中的 JDNI 查找)
- 在bean中添加了@Remove方法
- 让 servlet doGet() 在调用其他 2 个方法后调用 @Remove
结果:同样的问题。实体在第一次服务调用后未被管理
拉凯什
假设 service
方法在 updateEntity
显示描述的行为之前被调用:service
方法的事务属性是默认的,这意味着,正如您在评论中指出的 updateEntity
,事务在 service
调用结束时提交。此提交使 cached
实体不受管理。我建议用 @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
注释 service
方法,它应该保持 cached
实体被管理,随后调用 updateEntity
找到被管理的实体。
通常,对于扩展的持久性上下文,外观 EJB 类 被注释为 @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
并且只有应该提交的 EJB 方法才会获得不同的事务属性(参见例如文章 Domain-driven design with Java EE 6) .
永远不要将 STATEFUL(依赖于会话)bean 注入 servlet(由许多用户使用 = 由许多会话使用)。如果您使用 CMT,事务的提交将在调用 @Remove 标记的方法之后执行。它在哪里?它可以是空的,但它对有状态的 bean 很重要。
但这将是一个可行的解决方案:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form>
<h:inputText value="#{employeeMB.emplyeeName}"/>
<h:commandButton value="Submit" action="#{employeeMB.onSubmit}"/>
</h:form>
</h:body>
</html>
@ManagedBean
@SessionScoped
@Getter
@Setter
public class EmployeeMB
{
private String employeeName;
@EJB
private EmployeeSB employeeSB;
public void onSubmit()
{
employeeSB.persist( employeeName );
employeeName = "";
}
}
@Stateless
@LocalBean
public class EmployeeSB
{
@PersistenceContext
private EntityManager em;
public void persist( String employeeName_ )
{
Employee e = new Employee();
e.setName( employeeName_ );
em.persist( e );
}
}
似乎是 WildFly 中的错误。所有文档(规范、书籍等)都指出扩展上下文旨在防止实体在事务结束时分离。
已归档的 Wildfly 问题:https://issues.jboss.org/browse/WFLY-6383