我有点困惑:我有一个带有JSF,EJB和JPA的Java 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
状态为非托管状态。但为什么?!
我是否总是必须在我的方法中添加检查以确保实体是受管理的?
答案 0 :(得分:4)
实体仅在与从DB获得的事务相同的事务中进行管理。
在@Stateless
EJB中,来自客户端的单个方法调用默认计为单个完整事务。所有嵌套的EJB方法调用都在同一事务中进行。但是一旦从客户端调用EJB方法返回到客户端(例如,JSF / CDI托管bean),事务就结束了。当该方法返回一个实体时,它就会变得不受管理。
当您将同一个非托管实体传回服务层时,它仍然无法管理,直到您在其上调用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)不需要从单个操作方法连续调用多个不同的方法。相反,将多个不同服务方法的这种特定序列重构和/或合并为单个服务方法。这可以保证作业将在单个交易中进行。