Подтвердить что ты не робот

Почему JPA действует, как мой Entity отделяется, если я пытаюсь удалить его, но не если я его отредактирую?

Итак, у меня есть базовый 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>

И вот как выглядит 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";
    }

}

И, наконец, ActorDao:

@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);
    }

}

Итак, как вы можете видеть, редактировать Actor вызывает em.merge(a), и это работает отлично. Однако em.remove(a) вернется:

Caused by: java.lang.IllegalArgumentException: Entity must be managed to call remove: [email protected], try merging the detached and try the remove again.

Даже если я попробую:

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

Я все еще получаю то же исключение.

Итак, как это работает для редактирования строки, но не для ее удаления?

Только так я мог заставить его работать:

public void deleteActor(Actor a){
    Actor actorToBeRemoved = getWithId(a.getActorId());
    em.remove(actorToBeRemoved);
}

Что я делаю неправильно или не могу понять?

4b9b3361

Ответ 1

Метод merge() выполняет следующие действия: он принимает отдельный объект, загружает прикрепленный объект с тем же идентификатором из базы данных, копирует состояние отсоединенного объекта в прикрепленный и возвращает прикрепленный объект. Как вы отмечаете в этом описании, отдельный объект не изменяется вообще и не привязывается. Вот почему вы получаете исключение.

Вы не получили бы его, если бы сделали

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

Тем не менее, слияние совершенно не нужно, поскольку все, что вы хотите сделать, - удалить объект. Последнее решение прекрасно, за исключением того, что оно действительно загружает объект из базы данных, что также не нужно. Вы просто должны сделать

public void deleteActor(Actor a){
    Actor actorToBeRemoved = em.getReference(Actor.class, a.getActorId());
    em.remove(actorToBeRemoved);
}

Обратите внимание, что ваш метод getWithId() неэффективен и излишне сложный. Вы должны заменить его на

public Actor getWithId(int id){
    return em.find(Actor.class, id);
}

который будет использовать кеш первого уровня (и, возможно, второй уровень), чтобы избежать ненужных запросов.