JPA 和 EJB 托管与非托管
JPA and EJB managed vs. non-managed
我有点困惑:我有一个 Java 带有 JSF、EJB 和 JPA 的 EE 应用程序。
我有一个 UserService
,它是一个 EJB
。
@Stateless
public class UserService {
public User create(User u) throws ProcessingException {
if (!exists(u)) {
u = userDao.create(u);
addRole(u, RoleType.USER);
return u;
} else {
throw new ProcessingException("User " + u.getUsername() + " already exists");
}
}
public boolean hasRole(User u, RoleType r) {
if (u == null || r == null) {
return false;
}
if (!userDao.isManaged(u)) {
u = userDao.find(u.getId());
}
Set<Role> roles = u.getRoles();
...
}
}
我遇到了一些问题并进行了一些调试,发现有时在 hasRole
中,User
不处于托管状态,所以我 userDao.isManaged(u)
。但是我不明白为什么它有时不受管理。你能解释一下为什么吗?
示例:
@Test
public void test() throws ProcessingException {
Client c = clientBuilder.build();
User u = new User();
u.setClient(c);
userService.create(u);
userService.addRole(u, RoleType.APPROVER);
调用 addRole(u, RoleType.APPROVER)
时,u
的状态为非托管状态。但为什么?!
我是否总是必须向我的方法添加检查以确保实体受到管理?
实体仅在与从 DB 获取实体的事务完全相同的事务中进行管理。
在 @Stateless
EJB 中,来自客户端的单个方法调用默认计为单个完整事务。所有嵌套的 EJB 方法调用都发生在同一个事务中。但是一旦 EJB 方法从客户端 returns 调用到客户端(例如,JSF/CDI 托管 bean),事务就结束了。当那个方法 returns 一个实体时,它就变成了非托管的。
当您将完全相同的非托管实体传递回服务层时,它仍然是非托管的,直到您对其调用 em.merge()
,或者通过 em.find()
在 @Id
.
在您的具体情况下,您可以更改 create()
服务方法,如下所示:
public User create(User user, RoleType... roles) {
// ...
for (RoleType role : roles) {
addRole(user, role);
}
return user;
}
因此您可以在单个事务中执行该作业
userService.create(u, RoleType.APPROVER);
而不是在两个事务中
userService.create(u);
userService.addRole(u, RoleType.APPROVER);
EJB 方法的设计方式应使客户端(JSF/CDI 托管 bean)不需要从单个操作方法连续调用多个不同的方法。相反,重构 and/or 将多个不同服务方法的特定序列合并为一个服务方法。这保证了作业将在单个事务中进行。
另请参阅:
- When is it necessary or convenient to use Spring or EJB3 or all of them together?
我有点困惑:我有一个 Java 带有 JSF、EJB 和 JPA 的 EE 应用程序。
我有一个 UserService
,它是一个 EJB
。
@Stateless
public class UserService {
public User create(User u) throws ProcessingException {
if (!exists(u)) {
u = userDao.create(u);
addRole(u, RoleType.USER);
return u;
} else {
throw new ProcessingException("User " + u.getUsername() + " already exists");
}
}
public boolean hasRole(User u, RoleType r) {
if (u == null || r == null) {
return false;
}
if (!userDao.isManaged(u)) {
u = userDao.find(u.getId());
}
Set<Role> roles = u.getRoles();
...
}
}
我遇到了一些问题并进行了一些调试,发现有时在 hasRole
中,User
不处于托管状态,所以我 userDao.isManaged(u)
。但是我不明白为什么它有时不受管理。你能解释一下为什么吗?
示例:
@Test
public void test() throws ProcessingException {
Client c = clientBuilder.build();
User u = new User();
u.setClient(c);
userService.create(u);
userService.addRole(u, RoleType.APPROVER);
调用 addRole(u, RoleType.APPROVER)
时,u
的状态为非托管状态。但为什么?!
我是否总是必须向我的方法添加检查以确保实体受到管理?
实体仅在与从 DB 获取实体的事务完全相同的事务中进行管理。
在 @Stateless
EJB 中,来自客户端的单个方法调用默认计为单个完整事务。所有嵌套的 EJB 方法调用都发生在同一个事务中。但是一旦 EJB 方法从客户端 returns 调用到客户端(例如,JSF/CDI 托管 bean),事务就结束了。当那个方法 returns 一个实体时,它就变成了非托管的。
当您将完全相同的非托管实体传递回服务层时,它仍然是非托管的,直到您对其调用 em.merge()
,或者通过 em.find()
在 @Id
.
在您的具体情况下,您可以更改 create()
服务方法,如下所示:
public User create(User user, RoleType... roles) {
// ...
for (RoleType role : roles) {
addRole(user, role);
}
return user;
}
因此您可以在单个事务中执行该作业
userService.create(u, RoleType.APPROVER);
而不是在两个事务中
userService.create(u);
userService.addRole(u, RoleType.APPROVER);
EJB 方法的设计方式应使客户端(JSF/CDI 托管 bean)不需要从单个操作方法连续调用多个不同的方法。相反,重构 and/or 将多个不同服务方法的特定序列合并为一个服务方法。这保证了作业将在单个事务中进行。
另请参阅:
- When is it necessary or convenient to use Spring or EJB3 or all of them together?