Слияние иерархии прав доступа JPA приводит к исключению "Несколько представлений одного и того же объекта"
У меня есть иерархия сущностей, которая представлена и редактируется в веб-интерфейсе и сохраняется на уровне данных моего веб-приложения. Чтобы упростить сохранение, я аннотировал некоторые объекты с помощью CascadaType.PERSIST и CascadeType.MERGE - но это приводит к исключениям при сохранении и слиянии!
Здесь лица:
- У Адресса есть Земля
- Anfrage имеет адрес
- У Angebot есть адреса adresse.land и anfrage.adresse.land
Классы сущностей генерируются из некоторого источника XML, поэтому было бы трудно определить, какие сущности должны использовать каскадирование, а какие нет!
@Entity
public class Adresse {
@Id
@Column(name = "id", nullable = false)
@TableGenerator(name = "ids", table = "schema.ids", pkColumnName = "id_name", pkColumnValue = "ID", valueColumnName = "id", initialValue = 10000, allocationSize = 1)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "ids")
public Long getId() { return this.id; }
@ManyToOne(fetch= FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name="land_id")
public Land getLand() { return land; }
}
@Entity
public class Anfrage {
@Id
@Column(name = "id", nullable = false)
@TableGenerator(name = "ids", table = "schema.ids", pkColumnName = "id_name", pkColumnValue = "ID", valueColumnName = "id", initialValue = 10000, allocationSize = 1)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "ids")
public Long getId() { return this.id; }
@ManyToOne(fetch= FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name="adresse_id")
public Adresse getAdresse() { return adresse; }
}
@Entity
public class Angebot {
@Id
@Column(name = "id", nullable = false)
@TableGenerator(name = "ids", table = "schema.ids", pkColumnName = "id_name", pkColumnValue = "ID", valueColumnName = "id", initialValue = 10000, allocationSize = 1)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "ids")
public Long getId() { return this.id; }
@ManyToOne(fetch= FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name="adresse_id")
public Adresse getAdresse() { return adresse; }
@ManyToOne(fetch= FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name="anfrage_id")
public Anfrage getAnfrage() { return anfrage; }
}
Вот сервис для Anfrage - аналогичный доступен для Angebot и Adresse:
@DomainService
public class AnfrageService {
@Inject protected Repository repository;
public Anfrage getById(final Long id) {
return repository.getEntity(this.entityClass, id);
}
public void save(Anfrage entity) {
repository.save(entity);
}
public Anfrage update(final Anfrage entity) {
return repository.update(entity);
}
}
Здесь хранилище - представление уровня доступа к данным
@ApplicationScoped
@Transactional(value = Transactional.TxType.SUPPORTS)
public class Repository {
@Inject protected EntityManager entityManager;
public <T> T getEntity(final Class<T> clazz, final Object id) {
return entityManager.find(clazz, id);
}
@Transactional(value = Transactional.TxType.REQUIRED)
public <T> T update(T entity) {
return entityManager.merge(entity);
}
@Transactional(value = Transactional.TxType.REQUIRED)
public <T> void save(T entity) {
entityManager.persist(entity);
}
}
И вот мой Arquillian Test - те же ошибки / исключения появляются в приложении
@Test
public void saveNewAngebotWithExistingAnfrageWithExistingAdresse() {
final Long adresseID = 60L;
final Long anfrageID = 8811L;
Adresse adresse = adresseService.getById(adresseID); // -> returns adresse with land.id=1000
Anfrage anfrage = anfrageService.getById(anfrageID); // -> anfrage also has adresse with land.id=1000
Angebot angebot = AngebotFactory.create();
angebot.setBeschreibung("Testangebot");
angebot.setAdresse(adresse);
angebot.setAnfrage(anfrage);
angebotService.save(angebot);
// -> javax.persistence.PersistenceException: org.hibernate.PersistentObjectException:
// detached entity passed to persist: my.domain.model.impl.Anfrage
Angebot persistedAngebot = angebotService.update(angebot);
// -> java.lang.IllegalStateException: Multiple representations of the same entity
// [my.domain.model.impl.Land#1000] are being merged.
// Detached: [my.domain.model.impl.Land@273e9761[id=1000,version=6,created=17.05.2016 16:25:28,modified=17.05.2016 16:25:28,i18nKey=land.oesterreich,isoCode=AT,intVorwahl=43]];
// Detached: [my.domain.model.impl.Land@7c939d12[id=1000,version=6,created=17.05.2016 16:25:28,modified=17.05.2016 16:25:28,i18nKey=land.oesterreich,isoCode=AT,intVorwahl=43]]
}
Использование save / persist приводит к исключению, говорящему, что anfrage.angebot не может быть сохранен, потому что он отключен!
Использование update / merge приводит к исключению, говорящему, что anfrage.adresse.land и angebot.land являются одной и той же сущностью и поэтому не могут быть объединены!
Нужно ли обрабатывать постоянство / слияние вручную для моей иерархии сущностей?
Я думал, что каскадирование PERSIST и MERGE облегчает ситуацию, но, похоже, приводит к неприятностям?!
Я использую Wildfly 9, но получил те же ошибки с Wildfly 10!
Любые намеки приветствуются - спасибо!