Jakarta ee JPA和EJB托管与非托管

Jakarta ee JPA和EJB托管与非托管,jakarta-ee,jpa,ejb,entity,managed,Jakarta Ee,Jpa,Ejb,Entity,Managed,我有点困惑:我有一个带有JSF、EJB和JPA的JavaEE应用程序 我有一个UserService,它是一个EJB @Stateless public class UserService { public User create(User u) throws ProcessingException { if (!exists(u)) { u = userDao.create(u); addRole(u, RoleType.

我有点困惑:我有一个带有JSF、EJB和JPA的JavaEE应用程序

我有一个
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();
        ...
    }
}
当调用
addRole(u,RoleType.APPROVER)
时,则
u
的状态为非托管。但为什么


我是否总是必须在方法中添加检查,以确保该实体得到管理?

一个实体只在从DB获得的同一事务中被管理

在无状态的EJB中,来自客户端的单个方法调用默认情况下计为单个完整事务。所有嵌套EJB方法调用都发生在同一事务中。但是一旦来自客户机的EJB方法调用返回到客户机(例如,JSF/CDI托管bean),事务就结束了。当该方法返回实体时,它将变为非托管

当您将同一个非托管实体传递回服务层时,它仍然处于非托管状态,直到您在其上调用
em.merge()
,或者在
@Id
上通过
em.find()
从DB获取一个新实体

在您的特定情况下,您可以更改
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)不需要从单个操作方法连续调用多个不同的方法。相反,重构和/或将多个不同服务方法的特定序列合并到单个服务方法中。这保证了作业将在单个事务中进行

另见:

一个实体只在从DB获得的事务中进行管理

在无状态的EJB中,来自客户端的单个方法调用默认情况下计为单个完整事务。所有嵌套EJB方法调用都发生在同一事务中。但是一旦来自客户机的EJB方法调用返回到客户机(例如,JSF/CDI托管bean),事务就结束了。当该方法返回实体时,它将变为非托管

当您将同一个非托管实体传递回服务层时,它仍然处于非托管状态,直到您在其上调用
em.merge()
,或者在
@Id
上通过
em.find()
从DB获取一个新实体

在您的特定情况下,您可以更改
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)不需要从单个操作方法连续调用多个不同的方法。相反,重构和/或将多个不同服务方法的特定序列合并到单个服务方法中。这保证了作业将在单个事务中进行

另见:

但是方法呢,hasRole(User,RoleType)或updateRole(User,RoleType)。在这里,我总是需要调用find()或merge(),对吗?在hasRole中,只有当关系是延迟加载的,并且父实体是在不同的事务中获得的时,才有必要调用find()或merge()。在updateRole中,由于您最终已经执行了
em.merge()
,所以不需要对其进行管理,所以当服务方法返回时,所有更改都将被持久化。但是,方法hasRole(用户,角色类型)或updateRole(用户,角色类型)又如何呢。在这里,我总是需要调用find()或merge(),对吗?在hasRole中,只有当关系是延迟加载的,并且父实体是在不同的事务中获得的时,才有必要调用find()或merge()。在updateRole中,由于您最终已经执行了
em.merge()
,所以不需要对其进行管理,所以当服务方法返回时,所有更改都将被持久化。