Слияние иерархии прав доступа 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!

Любые намеки приветствуются - спасибо!

0 ответов

Другие вопросы по тегам