如果我尝试删除它,为什么JPA就像我的实体一样是分离的,但如果我编辑它则不会​​分离?

Kor*_*gay 17 jsf jpa

所以我有一个基本的JSF Datatable,相关部分是:

<h:dataTable value="#{actorTableBackingBean.allActors}" var="actor">

    <h:column headerText="Actor Name" sortBy="#{actor.firstName}">
        <h:outputText value="#{actor.firstName}" />
    </h:column>

    <h:column>
        <h:form>
            <h:commandButton value="Delete Actor"
                             action="#{actorTableBackingBean.deleteActor(actor)}"/>
        </h:form>
    </h:column>

    <h:column>
        <h:form>
            <h:commandButton value="Randomize Actor Name"
                             action="#{actorTableBackingBean.editActor(actor)}"/>
        </h:form>
    </h:column>

</h:dataTable>
Run Code Online (Sandbox Code Playgroud)

这就是ActorTableBackingBean的样子:

@Named
@RequestScoped
public class ActorTableBackingBean implements Serializable {

    @Inject
    ActorDao actorDao;

    private List<Actor> allActors;

    public List<Actor> getAllActors() {
        return allActors;
    }

    @PostConstruct
    public void fillUp(){
        allActors = actorDao.getAllT();
    }

    public String deleteActor(Actor a){
        removeActor(a);
        return "/allActors.xhtml";
    }

    private String removeActor(Actor a){
        try{
            actorDao.deleteActor(a);
            return null;
        }catch (Exception e){
            return null;
        }
    }

    public String editActor(Actor actor){
        actor.setFirstName("SomeRandonName");
        actorDao.editActor(actor);
        return "/allActors.xhtml";
    }

}
Run Code Online (Sandbox Code Playgroud)

最后是演员道:

@Stateless
public class ActorDao extends GeneralDao<Actor> implements Serializable {

    @Override
    protected Class<Actor> getClassType() {
        return Actor.class;
    }

    @Override
    public Actor getWithId(int id){
        TypedQuery<Actor> typedQuery =
                em.createQuery("Select a From Actor a WHERE a.actorId =" + id,Actor.class);
        return typedQuery.getSingleResult();
    }

    public void editActor(Actor a){
        em.merge(a);
    }

    public void deleteActor(Actor a){
        em.remove(a);
    }

}
Run Code Online (Sandbox Code Playgroud)

所以你可以看到编辑Actor调用em.merge(a),这很好用.但是,em.remove(a)将返回:

Caused by: java.lang.IllegalArgumentException: Entity must be managed to call remove: com.tugay.sakkillaa.model.Actor@6ae667f, try merging the detached and try the remove again.
Run Code Online (Sandbox Code Playgroud)

即使我尝试:

 public void deleteActor(Actor a){
    em.merge(a); 
    em.remove(a);
 }
Run Code Online (Sandbox Code Playgroud)

我仍然得到同样的例外.

那么它如何用于编辑行,而不是用于删除它?

我唯一能让它发挥作用的方式是:

public void deleteActor(Actor a){
    Actor actorToBeRemoved = getWithId(a.getActorId());
    em.remove(actorToBeRemoved);
}
Run Code Online (Sandbox Code Playgroud)

我做错了什么或者无法理解的是什么?

JB *_*zet 41

merge()方法执行以下操作:它接受一个分离的实体,从数据库加载具有相同ID的附加实体,将分离的实体的状态复制到附加的实体,并返回附加的实体.正如您在本说明中所述,分离的实体根本不会被修改,也不会附加.这就是你得到例外的原因.

如果你这样做,你就不会得到它

public void deleteActor(Actor a){
    a = em.merge(a); // merge and assign a to the attached entity 
    em.remove(a); // remove the attached entity
}
Run Code Online (Sandbox Code Playgroud)

也就是说,合并是完全没必要的,因为您要做的就是删除实体.最后一个解决方案很好,除了它真正从数据库加载实体,这也是不必要的.你应该这样做

public void deleteActor(Actor a){
    Actor actorToBeRemoved = em.getReference(Actor.class, a.getActorId());
    em.remove(actorToBeRemoved);
}
Run Code Online (Sandbox Code Playgroud)

请注意你的 getWithId()方法效率不高,而且不必要地复杂.你应该用它替换它

public Actor getWithId(int id){
    return em.find(Actor.class, id);
}
Run Code Online (Sandbox Code Playgroud)

它将使用第一级缓存(也可能是第二级缓存)以避免不必要的查询.

  • 如果`em`不包含实体,则该语句向下发送到'em.remove(em.merge(entity))`,这意味着:删除由`em.merge(entity)`返回的实体.所以它确实删除了`em.merge()`返回的附加实体. (2认同)