J'utilise une base de données relationnelle utilisant un seul pk de colonne avec quelques tables imbriquées. Je dois ajouter un archivage simple à mon projet. L'archivage ne se produit que lorsque l'application atteint un état particulier. J'espérais donc copier mon objet Veille prolongée existant dans une nouvelle instance où la nouvelle instance serait enregistrée avec un nouvel ID tout en laissant l'objet existant intact. Je n'arrive pas à comprendre comment copier l'objet existant dans une nouvelle instance sans avoir à définir manuellement chaque nouveau champ d'instance. Est-ce que quelqu'un connaît un moyen simple de faire cela?
Je travaille aussi avec Hibernate et j'ai les mêmes exigences que vous. Ce que j'ai suivi était d'implémenter Cloneable
. Vous trouverez ci-dessous un exemple de code indiquant comment procéder.
class Person implements Cloneable {
private String firstName;
private String lastName;
public Object clone() {
Person obj = new Person();
obj.setFirstName(this.firstName);
obj.setLastName(this.lastName);
return obj;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Ou vous pourriez choisir une solution basée sur la réflexion, mais je ne le recommanderai pas. Consultez ce site Web pour plus de détails.
Il suffit de récupérer l'objet, de le détacher, de définir l'identifiant sur null et de le conserver.
MyEntity clone = entityManager.find(MyEntity.class, ID);
entityManager.detach(clone);
clone.setId(null);
entityManager.persist(clone);
Si votre objet a des relations oneToMany, vous devrez répéter l'opération pour tous les enfants mais en définissant votre ID d'objet parent (généré après l'appel persist
) au lieu de null.
Bien sûr, vous devrez supprimer tout CASCADE persist
sur vos relations OneToMany, sinon votre persistante créerait des doublons de tous les enfants dans les échecs de contraintes de base de données ou fk.
Regardez le lien suivant. un des mécanismes de clonage les plus puissants pouvant être utilisés de la manière la plus efficace avec hibernate
http://thoughtfulsoftware.blogspot.in/2013/05/using-variable-depth-copy-to-prevent.html
Vous pouvez cloner l'objet, effacer l'identifiant et le sauvegarder afin qu'un nouvel identifiant soit attribué.
alors
Person person = EmployeeDAO.get(empId);
Person newPersonCopy = person.createCopyWithNoId()
EmployeeDAO.add(newPersonCopy);
dans ce cas, createCopyWithNoId () créerait un clone de l'objet et définirait le champ Id sur null. Lorsque vous ajoutez maintenant le nouveau clone, le moteur d'hibernation le verra comme un nouvel objet le persistera et la base de données assignera une nouvelle clé primaire.
Veuillez noter que j’ai évité d’appeler la méthode clone car ce qui en sort n’est pas un clone exact car nous manipulons l’ID (en le définissant null);
Une autre solution consiste à ajouter un constructeur qui a pris un objet de type personne et créé un nouvel objet, mais n'a tout simplement pas défini le champ Id en le laissant à sa valeur par défaut, null. Encore une fois, vous persistez en utilisant le DAO.
Comme je l'ai expliqué dans cet article , l'utilisation de detach
ou du clonage en profondeur, comme le suggèrent d'autres personnes, n'est pas la solution à prendre pour cloner une entité. Si vous essayez de rendre ce processus complètement automatique, vous allez oublier que tous les attributs ne méritent pas d'être dupliqués.
Par conséquent, il vaut mieux utiliser un constructeur de copie et contrôler exactement quels attributs doivent être clonés.
Donc, si vous avez une entité Post
comme celle-ci:
@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<PostComment> 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<Tag> 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;
}
}
Cela n'a pas de sens de cloner la comments
lors de la duplication d'une Post
et de l'utiliser comme modèle pour une nouvelle:
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);
Ce que vous devez ajouter à l'entité Post
est un constructeur de 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());
}
Le constructeur de copie est donc le meilleur moyen de résoudre le problème du clonage/duplication d’entités.
Vous pouvez soit cloner si l'objet est clonable, soit définir une méthode/un constructeur pour l'objet que vous souhaitez copier en prenant un paramètre de ce dernier et en copiant tout ce dont vous avez besoin dans une nouvelle instance et en vous le renvoyant.