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

17

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

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

задан Koray Tugay 06.07.2013 в 15:44
источник

1 ответ

40

Метод 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);
}

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

    
ответ дан JB Nizet 06.07.2013 в 19:56
источник
  • Спасибо за этот ясный и великий ответ. –  Koray Tugay 06.07.2013 в 20:55
  • Извините, вы можете объяснить, как em.remove (em.contains (entity)? entity: em.merge (entity)); работает в ответе BalusC в: stackoverflow.com/questions/17027398/... Разве это не та же ситуация? Слияния не должно быть достаточно, я прав? –  Koray Tugay 20.07.2013 в 22:55
  • , если em не содержит сущность, это утверждение сводится к em.remove (em.merge (entity)), что означает: удалить объект, возвращенный em.merge (entity). Таким образом, он удаляет прикрепленный объект, возвращенный em.merge (). –  JB Nizet 20.07.2013 в 22:58
  • Спасибо, получил это ... –  Koray Tugay 20.07.2013 в 23:04
  • Метод em.get (класс <T> entityClass, Object primaryKey) недоступен. Предполагается ли это метод em.find (класс <T> entityClass, Object primaryKey) вместо этого (в последнем фрагменте кода)? –  Tiny 26.11.2014 в 15:55
Показать остальные комментарии