2009-07-09 3 views
14

Я хотел бы сделать глубокую копию объекта в JPA. Я нашел интересную дискуссию здесь: http://forums.java.net/jive/thread.jspa?messageID=253092&tstart=0Deep Copy in JPA

Было похоже, что предлагаемое решение состояло в том, чтобы установить все @ Id на ноль. Вот мой базовый код:


//Start a JPA session. 
EntityManager em= emf.createEntityManager(); 
em.getTransaction().begin(); 

//Get the object I want to copy. 
MyClass myObject=em.find(MyClass.class,id); 

//Use reflection to find @Id's and set them to zero for all @OneToMany and @OneToOne relations. 
//TODO: write the ugly recursive code to do this. 

//Hoping this will create a deep copy. 
em.merge(myObject); 

//Close the session. 
em.getTransaction().commit(); 
em.close(); 

Это хорошая стратегия? Может кто-нибудь есть этот код TODO уже написано, что они могут делиться ???

Спасибо!

+0

Ссылка не работает. Можете ли вы его обновить. – Kayser

+0

Вы действительно хотите сделать глубокую копию? Это может привести к дублированию всей базы данных. Я предпочел бы использовать копирование - утомительный, но мог бы спасти меня головной болью или, что еще хуже, сбой сервера в производстве. –

ответ

2

Я смог получить глубокую копию для работы, как описано в вопросе. Необходимо с нетерпением загрузить весь график и сбросить значение @ Id на нуль или ноль. Я обнаружил, что в Hibernate SessionFactory есть методы, помогающие в этом процессе.

Другие рекомендации, приведенные выше для глубоких копий, как представляется, не работают. Конечно, проблема могла быть между клавиатурой и стулом. Но сейчас он работает.

Спасибо всем!

+4

В основном вы сказали, что нам нужно с нетерпением загружать весь график, но не часто наш график имеет ленивые инициализации. Вы также отметили, что у SessionFactory есть методы, помогающие процессу сбросить значение @ id. Можете ли вы привести пример кода, потому что я столкнулся с той же проблемой, и это сводит меня с ума :) –

1

Зачем вам это нужно? Это похоже на взлом.

Это говорит Apache Commons BeanUtils содержит cloneBean() и copyProperties() методы, чтобы сделать (мелкий) объект копию. Чтобы сделать глубокую копию, вы можете написать метод, предложенный here.

+0

Я хочу сделать глубокие копии данных, хранящихся в базе данных, которые будут полностью независимы от объекта, из которого он был скопирован. Например: -Object1 - это глубокая копия Object2. -Object1 имеет ребенка (из @OneToMany), который изменяется. -Объект Object2 не должен изменяться. – User1

+2

Для deepCopy вы можете использовать SerializationUtils.clone() от apache commons – rozky

3

Я не очень уверен, что идентификация уже обработанных объектов является хорошей идеей, особенно. когда ваши объекты не имеют equals(), определяемых как равенство идентификаторов. Реализация JPA могла иметь управляемые объекты в некотором кеше и идти beserk при игре с идентификаторами объектов там.

Я считаю, что было бы безопаснее следовать за ответом Р.К. и выполнять реальное копирование объектов.

+0

Согласовано. Я работал над несколькими проектами, где мы попытались создать обобщенный способ сделать глубокую копию графа объектов. Проблема, с которой вы всегда сталкиваетесь, поскольку проект растет, заключается в том, что вам нужно скопировать разные части графа объектов для разных вариантов использования и/или разных наборов свойств внутри объекта. В конечном итоге становится легче просто написать логику самостоятельно, а не пытаться стать умнее с автоматическим глубоким клонированием. Кроме того, стирание идентификаторов почти наверняка нарушит некоторые реализации JPA. –

2

Если ваши объекты реализуют Serializable, вы можете использовать writeObject() и readObject(), чтобы сделать глубокую копию. У нас есть иерархия объектов передачи данных и поддерживать глубокие копии с помощью этого метода в абстрактном суперкласса (DTO):

/** 
* Reply a deep copy of this DTO. This generic method works for any DTO subclass: 
* 
*  Person person = new Person(); 
*  Person copy = person.deepCopy(); 
* 
* Note: Using Java serialization is easy, but can be expensive. Use with care. 
* 
* @return A deep copy of this DTO. 
*/ 
@SuppressWarnings("unchecked") 
public <T extends DTO> T deepCopy() 
{ 
    try 
    { 
     ObjectOutputStream oos = null; 
     ObjectInputStream ois = null; 
     try 
     { 
      ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
      oos = new ObjectOutputStream(bos); 
      oos.writeObject(this); 
      oos.flush(); 
      ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); 
      return (T) ois.readObject(); 
     } 
     finally 
     { 
      oos.close(); 
      ois.close(); 
     } 
    } 
    catch (ClassNotFoundException cnfe) 
    { 
     // Impossible, since both sides deal in the same loaded classes. 
     return null; 
    } 
    catch (IOException ioe) 
    { 
     // This has to be "impossible", given that oos and ois wrap a *byte array*. 
     return null; 
    } 
} 

(. Я уверен, что кто-то найдет причину, почему могут произойти эти исключения )

Другие библиотеки сериализации (например, XStream) могут использоваться аналогичным образом.

+3

Это не сделает копию полей, отмеченных ключевым словом переходного процесса. –

+0

@Sean: Спасибо, хороший момент. (В нашем случае это ванильные JavaBeans.) –

+2

@Sean: Так как переходные поля не попадают в БД, это похоже на правильное поведение. –

3

Я РЕШЕН ЭТО.

Я создал компонент, который делает весь этот процесс для вас на основе аннотаций пакета (javax.persistence).

Компонент уже устанавливает идентификатор объекта в null. Он выполняет весь анализ алгоритма, применяемого на основе типа отношения каждого атрибута @OneToMany, @OneToOne или @ManyToMany.

Пример

Person person = personDAO.find(1); 
PersistenceCloner cloner = new PersistenceCloner(person); 
Person personCopy = cloner.generateCopyToPersist(); 

Скачать JAR и ИСТОЧНИКИ: jpa-entitycloner