Почему 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);
}
Что я делаю неправильно или не могу понять?
Ответы
Ответ 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);
}
который будет использовать кеш первого уровня (и, возможно, второй уровень), чтобы избежать ненужных запросов.