Hibernate使用新生成的ID将对象值复制到新对象中

Cod*_*kie 27 java hibernate jpa

我正在使用带有一些嵌套表的单列pk的关系数据库.我需要在项目中添加简单的归档.归档仅在应用程序到达特定状态时发生,因此我希望将现有的hibernate对象复制到新实例中,新实例将使用新ID保存,同时保留现有对象.我似乎无法弄清楚如何将现有对象复制到新实例而无需手动设置每个新的实例字段.有人知道这样做的简单方法吗?

Gab*_*ica 40

只需检索对象,将其分离,将id设置为null并保持它.

MyEntity clone = entityManager.find(MyEntity.class, ID);
entityManager.detach(clone);
clone.setId(null);
entityManager.persist(clone);
Run Code Online (Sandbox Code Playgroud)

如果您的对象具有oneToMany关系,则必须为所有子项重复操作,但设置父对象id(在persist调用之后生成)而不是null.

当然,您必须删除CASCADE persistOneToMany关系中的任何关系,否则您的持久性将在DB或fk约束失败中创建所有子项的重复项.

  • @Legna这意味着它正在尝试处理非延迟加载的东西.初始化您需要首先克隆的子项.[请参阅此链接](http://stackoverflow.com/a/19928738/2241785)了解有关如何执行此操作的详细信息. (2认同)

Vla*_*cea 9

detach在克隆实体时,按照其他人的建议使用或深度克隆并不是要走的路。如果您尝试使此过程完全自动化,您将错过并非所有属性都值得复制这一点。

因此,最好使用复制构造函数并准确控制需要克隆的属性。

所以,如果你有一个Post这样的实体:

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

comments在复制 aPost并将其用作新的模板时克隆是没有意义的:

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);
Run Code Online (Sandbox Code Playgroud)

您需要添加到Post实体的是一个复制构造函数:

/**
 * 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());
}
Run Code Online (Sandbox Code Playgroud)

因此,复制构造函数是解决实体克隆/复制问题的最佳方法。


Cha*_*han 7

我也在使用Hibernate,我得到了同样的要求.我遵循的是实施Cloneable.下面是如何执行此操作的代码示例.

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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

或者你可以去基于反射的解决方案,但我不会建议.查看此网站了解更多详情.

  • 您如何处理嵌套表,例如 oneToMany 等?我担心我必须再次设置所有对象变量。:( (3认同)