JPA/Hibernate Несколько представлений одного и того же объекта

Мои сущности:

@Entity
public class Document  {

   @Id
   protected String id; //It string in purpose

   @OneToOne(cascade = ALL)
   @JoinColumn(name = "DOCUMENT_DETAILS")
   private DocumentDetails details;

} 

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "documentDiscr")
@EqualsAndHashCode
public abstract class DocumentDetails {

   @Id @GeneratedValue(strategy = GenerationType.SEQUENCE)
   private Long id;

   private Money total;

   @ManyToOne(cascade = CascadeType.ALL)
   @JoinColumn(name = "SELLER_ID")
   private Company seller;

   @ManyToOne(cascade = CascadeType.ALL)
   @JoinColumn(name = "BUYER_ID")
   private Company buyer;
}

@Entity
public class Company {

   @Id
   protected String id;

   private String name;
   private String phoneNumber;
   private String email;

   @OneToOne(cascade = CascadeType.ALL)
   @JoinColumn(name = "address_id")
   private Address address;
}

@Entity
@EqualsAndHashCode
public class Address {

   @Id
   @GeneratedValue(strategy= GenerationType.SEQUENCE)
   private Long id;
   private String country;
   private String city;
   private String postalCode;
   private String streetName;
   private String streetNumber;
   private String apartmentNumber;
}

@Path("path")
@Transactional 
public class MyResource {

   @Inject
   MyRepo myRepo;

   @PUT
   public Document updateDoc(Document document){
       myRepo.update(document);
   }

}

public class Repo<T extends MyClass> implements MyRepo<T> {

    @PersistenceContext
    protected EntityManager entityManager;


    public T create(T t) {
       t.generateId();
       this.entityManager.persist(t);
       return t;
    }

    public T update(T entity) {
       return entityManager.merge(entity);
    }

}

Когда я вызываю entityManage.update(documentEntity) и в качестве поставщика и покупателя добавляется одна и та же компания, я вижу 'Multiple representations of the same entity'.

Я прочитал это, но ничего не помогает. Когда я удалил CascadeType.All, я получаю

'detached entity passed to persist: my.pckg.Address'

Я также пытался удалить CascadeType.Merge, но ошибка та же. Что я могу сделать? Где моя ошибка?

ОБНОВЛЕНИЕ

Сначала я изменил @ManyToOne(Cascade.All) на @ManyToOne() в DocumentDetails

Во-вторых, я изменил @ManyToOne(Cascade.All) на @ManyToOne(Cascade.Merge) в DocumentDetails.

В-третьих, я изменил @ManyToOne(Cascade.All) на @ManyToOne(all types except all and merge) в DocumentDetails.

Я также пробовал то же самое с классом Address


person Krzysiek    schedule 27.01.2017    source источник
comment
Вы можете добавить транзакционный метод, который сохраняет сущность?   -  person Maciej Kowalski    schedule 27.01.2017
comment
также с каких маппрингов вы убрали каскад?   -  person Maciej Kowalski    schedule 27.01.2017
comment
Когда вы говорите, что одна и та же компания добавляется в качестве поставщика и покупателя, вы имеете в виду, что значения одинаковы или это фактически один и тот же объект?   -  person Douglas    schedule 27.01.2017
comment
@MaciejKowalski Я обновил пост :)   -  person Krzysiek    schedule 27.01.2017
comment
@Douglas Вероятно, те же значения, но не объекты. Я не уверен, как Джереси десериализует Json. В отладке я увидел, что эти объекты имеют разные ссылки, такие как Company@123 и Company@125.   -  person Krzysiek    schedule 27.01.2017
comment
какая у вас спящая / весенняя версия?   -  person Maciej Kowalski    schedule 28.01.2017
comment
@MaciejKowalski Я использую весеннюю загрузку, но даже когда я явно объявил в pom Hibernate 5.2.6.FINAL ничего не меняется   -  person Krzysiek    schedule 28.01.2017
comment
Вероятно, те же значения, но не объекты. - это первопричина. Я бы не ожидал, что спящий режим просто сохранит все, что было десериализовано из JSON. Здесь потребуется некоторая ручная работа, чтобы получить фактические данные из БД и иметь управляемые объекты. Например, вы не сможете сохранить новый документ, сопоставленный с существующей компанией. Каскадное сохранение не удастся на объекте компании. Попробуйте использовать Spring Data REST, чтобы сделать это за вас.   -  person Piotr Gwiazda    schedule 28.01.2017
comment
@PiotrGwiazda Спасибо за объяснение! Я посмотрю на Spring Data REST, но также попытаюсь выполнить ручную работу. Если вы можете посоветовать мне что-то, чтобы начать с ручного исправления, я буду очень признателен.   -  person Krzysiek    schedule 28.01.2017
comment
Попробуйте удалить аннотацию @EqualsAndHashCode из сущностей.   -  person Dherik    schedule 28.01.2017
comment
@Dherik Я добавил это, когда возникает проблема, но это не решает ее. Я оставил его, потому что подумал, что это хорошая идея.   -  person Krzysiek    schedule 29.01.2017
comment
Это происходит, когда вы перезаписываете объект с тем же значением, но с другим хэш-кодом.   -  person ABHAY JOHRI    schedule 18.12.2017
comment
Возможный дубликат java.lang.IllegalStateException: несколько представлений тот же объект с @ManyToMany 3 объекта   -  person Jens Schauder    schedule 27.08.2018


Ответы (2)


Хорошо - с подсказкой от Петра Гвязды я решил это.

Простое и наивное решение добавляется:

if(seller != null && buyer != null){
   if(seller.equals(buyer)){
       document.getDetails.setSeller(document.getDetails().getBuyer());
   }
}

(лучший ответ - SpringData)

до update(). Эта ситуация имеет место, потому что, когда два разных объекта равны, но они ссылаются на разные спящие режимы, они не могут обрабатывать их как один и тот же объект.

person Krzysiek    schedule 07.02.2017

Это происходит, когда вы перезаписываете объект с тем же значением, но с другим хэш-кодом. Если у меня есть класс employee, а у сотрудника есть класс address, и если я сохраню того же сотрудника с тем же значением адреса, но с другим хэш-кодом, тогда я получу эту проблему. Если я клонирую адрес и установлю сотрудника, то Hibernate запутается и не сможет сохранить данные

@Entity
@Getter
@Setter
class Person implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="id", unique = true, nullable = false)
    private Long id;

    @OneToMany(cascade= CascadeType.ALL)
    private Set<Address> Addresses = new HashSet<>();

    protected Person(){}
}

@Entity
@Getter
@Setter
class Address implements Serializable {
    @EmbeddedId
    private AddressId addressId;
    private State state;
    protected Address(){}
}

@Embeddable
@Getter
@Setter
public class AddressId implements Serializable {
    @Column(name = "person_id")
    private Long personId;
    @Column(name = "postcode")
    private String postcode;
}

Решение Для этого

AddressId addressId = new AddressId();
addressId.setPersonId(personId);
addressId.setPostcode("4000"); //person already has an address with this postcode
Address address = null;
for (Address a : person.getAddresses()) {
    if (a.getAddressId().equals(addressId)) {
        address = a;
        break;
    }
}
if (address == null) {
    address = new Address();
    address.setAddressId(addressId);
    address.setState(State.ABC); //but I want to change the state from QLD to TAS
    person.getAddresses().add(address);
}
else {
    address.setState(State.TAS);
}
person = personRepo.save(person);
person ABHAY JOHRI    schedule 15.11.2017