データベースに既に永続化されているJPAエンティティがあります。
一部のフィールドを変更して、コピー(異なるID)を取得したいと思います。
これを行う最も簡単な方法は何ですか?お気に入り:
@Id
フィールドをnull
に設定し、永続化すると動作しますか?@Id
を除くすべてのフィールドをコピーします)?使用する EntityManager.detach
。 BeanがEntityManagerにリンクされなくなります。次に、Idを新しいID(または自動の場合はnull)に設定し、必要なフィールドを変更して永続化します。
EclipseLinkを使用する場合、非常に便利なCopyGroup-Featureを使用できます。
http://wiki.Eclipse.org/EclipseLink/Examples/JPA/AttributeGroup#CopyGroup
大きなプラスは、あまり手間をかけることなく、プライベート所有の関係シップも適切に複製することです。
これは私のコードです。プライベート所有の@ OneToMany-relationshipでプレイリストのクローンを作成するのは数行の問題です:
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()は機能しません。
私は今日同じ問題に直面しています:私はデータベースにエンティティがあり、私はしたいです:
私は次の手順を実行することに成功します:
@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」コマンドの後にクローンIDが変更されていることに注意しても、「merge」ではなく「persist」を使用すると、最後のステップ(クローン永続性)は機能しません!
私がまだ直面している問題は、デタッチする前に最初のエンティティが変更されていないことです。
Orikaのようなマッピングフレームワークを使用できます。 http://orika-mapper.github.io/orika-docs/ Orikaは、あるオブジェクトから別のオブジェクトにデータを再帰的にコピーするBeanマッピングフレームワークJava構成が簡単で、さまざまな柔軟性も提供します。
私のプロジェクトでこれをどのように使用したかを以下に示します。dependecyを追加しました。
<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);
これは、このような種類のクローニングが必要なユースケースが多数ある場合に役立ちます。
受け入れられた回答へのコメントで述べたように、デタッチは管理対象エンティティへのフラッシュされていない変更を無視します。春を使用している場合は、_org.springframework.beans.BeanUtils
_を使用する別のオプションがあります
ここにBeanUtils.copyProperties(Object source, Object target)
があります。これにより、entityManagerを改ざんすることなく、浅いコピーを作成できます。
Edit:APIドキュメントからの引用:「このメソッドは、プロパティの複雑なプロパティ(ネストされたプロパティなど)の「浅いコピー」を実行するためのものです。 )はコピーされません。」
これ ブログの投稿では、ディープコピーの詳細についてJavaオブジェクト。
この記事 で説明したように、コピーコンストラクターを使用し、どの属性を複製する必要があるかを正確に制御することをお勧めします。
したがって、次のような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;
}
}
comments
を複製して新しいテンプレートのテンプレートとして使用する場合、Post
を複製することは意味がありません。
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
エンティティに追加する必要があるのは、コピーコンストラクターです。
/**
* 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());
}
これは、エンティティのクローン/複製の問題に対処する最良の方法です。このプロセスを完全に自動化しようとする他の方法では、すべての属性を複製する価値があるというわけではありません。
私はIDをnullに設定しようとしましたが、うまくいきました
address.setId(null);
address = addrRepo.save(address);
idをnullに設定すると、自動的に生成されるため、新しいIDで新しいレコードに保存されました。