I\'m using a relational DB using a single column pk with a few nested tables.
I need to add a simple archiving to my project. The archiving only happens when the appl
This is a very common question, so this answer is based on this article I wrote on my blog.
Using detach
or deep cloning as suggested by others are not the way to go when it comes to cloning an entity. If you try to make this process completely automatic, you are going to miss the point that not all attributes are worth duplicating.
Therefore, you are better off using a copy constructor and controlling exactly what attributes need to be cloned.
So, if you have a Post
entity like this one:
@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;
}
}
It does not make sense to clone the comments
when duplicating a Post
and using it as a template for a new one:
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);
What you need to add to the Post
entity is a copy constructor:
/**
* 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());
}
So, the copy constructor is the best way to address the entity clone/duplication problem.