Clonazione di quadro JPA

Ho già un’ quadro JPA persistente nel database.
Vorrei avere una copia di esso (con un ID diverso), con alcuni campi modificati.

Qual è il modo più semplice per farlo? Piace:

  • impostando il campo @Id su null e continuando a farlo funzionare?
  • dovrò creare un metodo clone per l’ quadro (copiando tutti i campi tranne il @Id)?
  • c’è qualche altro approccio (come usare un framework di clonazione)?

Utilizzare EntityManager.detach . Rende il bean non più collegato a EntityManager. Quindi imposta l’Id sul nuovo Id (o null se automatico), cambia i campi di cui hai bisogno e persistono.

Quando si utilizza EclipseLink, è ansible utilizzare la funzione CopyGroup MOLTO pratica:

http://wiki.eclipse.org/EclipseLink/Examples/JPA/AttributeGroup#CopyGroup

Un grande vantaggio è che senza molto clamore clona correttamente anche le relazioni di proprietà privata.

Questo è il mio codice, clonare una playlist con la sua relazione privata OneToMany è questione di poche righe:

 public Playlist cloneEntity( EntityManager em ) { CopyGroup group = new CopyGroup(); group.setShouldResetPrimaryKey( true ); Playlist copy = (Playlist)em.unwrap( JpaEntityManager.class ).copy( this, group ); return copy; } 

Assicurati di usare persist () per salvare questo nuovo object, l’unione () non funziona.

Potresti usare strutture di mapping come Orika. http://orika-mapper.github.io/orika-docs/ Orika è un framework di mapping dei bean Java che copia ricorsivamente i dati da un object all’altro. È facile da configurare e offre anche varie flessibilità.

Ecco come l’ho usato nel mio progetto: aggiunta una dipendenza:

   ma.glasnost.orika orika-core 1.4.6  

Quindi utilizzalo nel codice come segue:

 MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build(); MapperFacade mapper=mapperFactory.getMapperFacade(); User mappedUser = mapper.map(oldUser, User.class); 

Questo potrebbe aiutare se hai molti casi in cui è necessario un tale tipo di clonazione.

Oggi ho lo stesso problema: ho un’entity framework nel database e voglio:

  • scaricarlo dal database
  • cambia uno dei suoi attributi valore
  • crea un clone di esso
  • modificare solo alcuni attributi del clone
  • clone persistente nel database

Sono riuscito a fare i seguenti passi:

 @PersistenceContext(unitName = "...") private EntityManager entityManager; public void findUpdateCloneAndModify(int myEntityId) { // retrieve entity from database MyEntity myEntity = entityManager.find(MyEntity.class, myEntityId); // modify the entity myEntity.setAnAttribute(newValue); // update modification in database myEntity = entityManager.merge(myEntity); // detach entity to use it as a new entity (clone) entityManager.detach(myEntity); myEntity.setId(0); // modify detached entity myEntity.setAnotherAttribute(otherValue); // persist modified clone in database myEntity = entityManager.merge(myEntity); } 

Nota : l’ultimo passaggio (clone persistence) non funziona se uso ‘persist’ invece di ‘unire’, anche se noto in modalità debug che l’id clone è stato cambiato dopo il comando ‘persist’!

Il problema che sto ancora affrontando è che la mia prima quadro non è stata modificata prima di distaccarla.

Come menzionato nei commenti alla risposta accettata, detatch ignorerà le modifiche non aggiornate all’ quadro gestita. Se si utilizza Spring, è disponibile un’altra opzione che utilizza org.springframework.beans.BeanUtils

Qui hai BeanUtils.copyProperties(Object source, Object target) . Questo ti permetterà di fare una copia completa senza manomettere l’entityManager.

Come ho spiegato in questo articolo , è meglio usare un costruttore di copie e controllare esattamente quali attributi devono essere clonati.

Quindi, se hai un’ quadro Post come questa:

 @Entity(name = "Post") @Table(name = "post") public class Post { @Id @GeneratedValue private Long id; private String title; @OneToMany( mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true ) private List comments = new ArrayList<>(); @OneToOne( mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY ) private PostDetails details; @ManyToMany @JoinTable( name = "post_tag", joinColumns = @JoinColumn( name = "post_id" ), inverseJoinColumns = @JoinColumn( name = "tag_id" ) ) private Set tags = new HashSet<>(); //Getters and setters omitted for brevity public void addComment( PostComment comment) { comments.add(comment); comment.setPost(this); } public void addDetails( PostDetails details) { this.details = details; details.setPost(this); } public void removeDetails() { this.details.setPost(null); this.details = null; } } 

Non ha senso clonare i comments durante la duplicazione di un Post e utilizzarlo come modello per uno nuovo:

 Post post = entityManager.createQuery( "select p " + "from Post p " + "join fetch p.details " + "join fetch p.tags " + "where p.title = :title", Post.class) .setParameter( "title", "High-Performance Java Persistence, 1st edition" ) .getSingleResult(); Post postClone = new Post(post); postClone.setTitle( postClone.getTitle().replace("1st", "2nd") ); entityManager.persist(postClone); 

Quello che devi aggiungere all’ quadro Post è un costruttore di copie:

 /** * Needed by Hibernate when hydrating the entity * from the JDBC ResultSet */ private Post() {} public Post(Post post) { this.title = post.title; addDetails( new PostDetails(post.details) ); tags.addAll(post.getTags()); } 

Questo è il modo migliore per affrontare il problema dell’ quadro clone / duplicazione. Qualsiasi altro metodo, che tenta di rendere questo processo completamente automatico, non tiene conto del fatto che non tutti gli attributi meritano di essere duplicati.