如何克隆JPA实体

hk8txs48  于 2022-12-04  发布在  其他
关注(0)|答案(8)|浏览(204)

我已经在数据库中持久化了一个JPA实体。
我想有它的副本(与不同的ID),与一些字段修改。
最简单的方法是什么?例如:

  • 将它的@Id字段设置为null并保持它是否有效?
  • 我是否必须为实体创建一个clone方法(复制除@Id之外的所有字段)?
  • 是否有其他方法(如使用克隆框架)?
odopli94

odopli941#

使用EntityManager.detach。它使bean不再链接到EntityManager。然后将Id设置为新的Id(如果自动设置,则为null),更改所需的字段并保持。

ykejflvf

ykejflvf2#

最好使用复制构造函数并精确控制需要克隆的属性。
因此,如果有一个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;
    }
}

复制Post并将其用作新模板时,克隆comments没有意义:

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

需要添加到Post实体的是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());
}

这是解决实体克隆/复制问题的最佳方法。试图使此过程完全自动化的任何其他方法都忽略了并非所有属性都值得复制这一点。

vwkv1x7d

vwkv1x7d3#

在使用EclipseLink时,您可以使用非常方便的CopyGroup-Feature:
http://wiki.eclipse.org/EclipseLink/Examples/JPA/AttributeGroup#CopyGroup
一个很大的好处是,没有太多的拨弄它适当地克隆私人拥有的关系,太。
这是我的代码,克隆一个具有私有@ OneToMany关系的播放列表只需要几行代码:

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

确保使用persist()保存这个新对象,merge()不起作用。

ql3eal8s

ql3eal8s4#

我今天面临同样的问题:我在数据库中有一个实体,我想:

  • 从数据库获取
  • 更改其一个属性值
  • 创建其克隆
  • 仅修改克隆的一些属性
  • 在数据库中保留克隆

我成功地完成了以下步骤:

@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);
}

备注:如果我使用'persist'而不是'merge',最后一步(克隆持久性)不起作用,即使我在调试模式中注意到克隆id在'persist'命令后已更改!

我仍然面临的问题是,在分离第一个实体之前,尚未对其进行修改。

kh212irz

kh212irz5#

您可以使用Orika这样的Map框架。http://orika-mapper.github.io/orika-docs/ Orika是一个Java beanMap框架,可以递归地将数据从一个对象复制到另一个对象。它易于配置,并提供了各种灵活性。
以下是我在项目中使用它的方式:添加了一个依赖项:

<dependency>
      <groupId>ma.glasnost.orika</groupId>
      <artifactId>orika-core</artifactId>
      <version>1.4.6</version>
</dependency>

然后在代码中使用它,如下所示:

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

如果您有许多需要这种克隆的用例,这可能会有所帮助。

ryevplcw

ryevplcw6#

正如已接受答案的注解中提到的,detatch将忽略对托管实体的未刷新更改。
这里有BeanUtils.copyProperties(Object source, Object target)。这将允许您在不篡改entityManager的情况下进行浅层复制。

**编辑:**引用自api文档:此方法用于执行属性得“浅层复制,“因此不会复制复杂属性(例如,嵌套属性).

This博客文章可能会告诉你更多关于深度复制java对象的信息。

o2rvlv0m

o2rvlv0m7#

我只是尝试将id设置为null,并且成功了

address.setId(null);
address = addrRepo.save(address);

将id设置为null使其保存到具有新id的新记录中,因为我已自动生成它。

bweufnob

bweufnob8#

ModelMapper库可用于此目的。

public MyEntity clone(MyEntity myInstance) {
    MyEntity newInstance = new MyEntity();
    new ModelMapper().map(myInstance, newInstance);
    return newInstance;
}

只需添加Maven依赖项

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>2.3.2</version>
</dependency>

相关问题