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?