2009-07-09 11 views
14

Me gustaría hacer una copia profunda de una entidad en JPA. Encontré una discusión interesante aquí: http://forums.java.net/jive/thread.jspa?messageID=253092&tstart=0Copia profunda en JPA

Parecía que la solución propuesta era establecer todos los Id @ a cero. Aquí está mi código básico:


//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(); 

¿Es esta una buena estrategia? ¿Alguien podría tener este código TODO ya escrito que puedan compartir?

Gracias!

+0

El enlace está roto. Puedes actualizarlo – Kayser

+0

¿Estás seguro de que quieres hacer una copia profunda? Eso podría llevar a tener toda la base de datos duplicada. Preferiría seguir con la implementación de la copia: tedioso, pero podría ahorrarme un dolor de cabeza o, peor aún, un servidor que se bloquea en la producción. –

Respuesta

2

Pude obtener una copia profunda para trabajar como se describe en la pregunta. Es necesario cargar ansiosamente todo el gráfico y restablecer @ Id a nulo o cero. Descubrí que Hibernate SessionFactory en realidad tiene métodos para ayudar con este proceso.

Las otras recomendaciones anteriores para copias profundas no parecían funcionar. De acuerdo, el problema puede haber sido entre el teclado y la silla. Pero está funcionando ahora.

¡Gracias a todos!

+4

Básicamente dijiste que tenemos que cargar ansiosamente todo el gráfico, pero no a menudo nuestro gráfico tiene colecciones perezosas inicializadas. También comentó que SessionFactory tiene métodos para ayudar con el proceso para restablecer los @ id. ¿Puedes dar algún ejemplo de código, porque estoy enfrentando el mismo problema y me está volviendo loco? :) –

1

¿Por qué querrías hacer esto? Suena un poco como piratear.

Dicho Apache Commons BeanUtils contiene cloneBean() y copyProperties() métodos para hacer (poco profundas) copias de objetos. Para hacer una copia profunda puede escribir un método como el propuesto here.

+0

Quiero hacer copias en profundidad de los datos almacenados en la base de datos que serán completamente independientes del objeto desde el que se copió. Por ejemplo: -Object1 es una copia profunda de Object2. -Objeto1 tiene un hijo (de @OneToMany) que cambia. -El hijo de Objeto2 no debe cambiar. – User1

+2

Para deepCopy puede usar SerializationUtils.clone() de Apache commons – rozky

3

No estoy seguro de si la puesta a cero de identificadores de objetos ya administrados es una buena idea, esp. cuando sus entidades no tienen equals() definido como igualdad de ID. La implementación de JPA podría haber tenido los objetos administrados en alguna memoria caché e ir a beserk cuando se juega con ID de objetos allí.

Creo que sería más seguro seguir la respuesta de R.K. y hacer la copia real de los objetos.

+0

de acuerdo. He trabajado en un par de proyectos donde tratamos de encontrar una forma generalizada de hacer una copia profunda de un gráfico de objetos. El problema con el que siempre se encuentra a medida que crece el proyecto es que termina necesitando copiar diferentes partes del gráfico de objetos para diferentes casos de uso y/o diferentes conjuntos de propiedades dentro de un objeto. En última instancia, resulta más fácil escribir la lógica por sí mismo en lugar de intentar ser inteligente con la clonación profunda automática. Además, la eliminación de las ID casi con seguridad romperá algunas implementaciones de JPA. –

2

Si sus objetos implementan Serializable, puede usar writeObject() y readObject() para hacer una copia profunda. Tenemos una jerarquía de objetos de transferencia de datos y soporte copias profundas a través de este método en la superclase abstracta (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; 
    } 
} 

(. Estoy seguro de que alguien va a encontrar una razón por la cual estas excepciones pueden ocurrir )

Se podrían usar otras bibliotecas de serialización (por ejemplo, XStream) de la misma manera.

+3

Esto no hará una copia de los campos marcados con la palabra clave transitoria. –

+0

@Sean: Gracias, un buen punto. (En nuestro caso, estos son JavaBeans vainilla). –

+2

@Sean: dado que los campos transitorios no terminarán en el DB, tampoco, ese parece ser el comportamiento correcto. –

3

RESOLVIÉ ESTO.

Creé un componente que hace que todo este proceso para usted basado en las anotaciones del paquete (javax.persistence).

El componente ya establece el id de la entidad como nulo. Él hace todo el análisis del algoritmo que se aplica en función del tipo de cada relación de atributo @OneToMany, @OneToOne o @ManyToMany.

Ejemplo

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

Descargar JAR y fuentes: jpa-entitycloner

Cuestiones relacionadas