Как игнорировать повторное использование ссылок на объекты из нагрузки в спящем режиме? - PullRequest
1 голос
/ 30 октября 2019

Допустим, у меня есть следующее отображение Java ORM:

@Entity
public class Person {

  @Id
  @GeneratedValue
  private Long id;

  @OneToOne
  private Address workAddress;

  @OneToOne
  private Address homeAddress;

}

@Entity
public class Address {

  @Id
  @GeneratedValue
  private Long id;

}

И скажем, что данный экземпляр Person был создан с использованием того же постоянного Address экземпляра. Например:

@Transactional
public void reuseAddressInPerson() {
   Address address = new Address();
   // fill address values
   address = em.persist(address);
   Person person = new Person();
   person.workAddress = address;
   person.homeAddress = address;
   em.persist(person);
}

Исходя из этого, когда я загружаю это постоянное person с использованием hibernate, экземпляры адресов для workAddress и homeAddress будут одинаковыми ссылками в объекте person. Есть некоторый способ заставить hibernate создавать новые экземпляры для каждого объекта address при вызове em.find()?

Примечание: я создал этот пример, иллюстрирующий мой вопрос, но в реальном сценарии его нецелесообразно использоватьклон (или что-то подобное), чтобы подготовить этот объект.

1 Ответ

0 голосов
/ 31 октября 2019

По-видимому, hibernate не предоставляет никакого готового механизма для управления ссылками на сущности. Поэтому я нашел способ сделать это, используя ObjectOutputStream. Этот механизм требует двух шагов: первый загружает объект из базы данных и очищает контекст постоянства, а второй ищет в структуре объекта, какие объекты следует клонировать для удаления общих ссылок. Чтобы объяснить, как это работает, мы будем использовать ту же модель адреса человека из вопроса.

Шаг 1 - Загрузка объекта

public Person loadProject() {
  // First we load person from database
  Person person = personRepository.getOne(personId);
  // Then we clear the persistence context to evict detached exceptions
  entityManager.clear();
  person = (Person) Hibernate.unproxy(person); // Remove proxies from the object
  // Now we call Step 2 (clone shared references)
  person = entityReferencesService.cloneAddressReferences(person);
  return person;
}

Шаг 2 - Клонирование общих ссылок


public class EntityReferencesService {
  //...
  public Person cloneAddressReferences(Person person) {
    try {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(baos) {
        {
          enableReplaceObject(true); // Allows objects to be replaced during serialization
        }
        @Override
        protected Object replaceObject(Object obj) throws IOException {
          Object replacedObject = obj;
          // This code could be smart to known what entities to clone
          if (obj instanceof Person) {
            Person p = (Person) obj;
            // using apache bean commons
            p.workAddress = SerializationUtils.clone(p.workAddress);
            p.homeAddress = SerializationUtils.clone(p.homeAddress);
          }
          return replacedObject;
        }
      };
      oos.writeObject(person);
      ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
      ObjectInputStream ois = new ObjectInputStream(bais);
      return (Person) ois.readObject();
    } catch (Exception ex) {}
  }
} 

Этот пример слишком упрощен. Но для сложных сценариев в методе replaceObject этот код должен использовать интерфейсы или аннотации, чтобы знать, какие объекты клонируются.

...