web-gelistirme-sc.com

JPA varlığını klonlama

Veritabanında zaten devam eden bir JPA varlığım var.
Bazı alanları değiştirilmiş halde (farklı bir kimliğe sahip) bir kopyasını almak istiyorum.

Bunu yapmanın en kolay yolu nedir? Sevmek:

  • @Id alanını null olarak ayarlamak ve devam etmek işe yarayacak mı?
  • varlık için bir klonlama yöntemi oluşturmak zorunda mıyım (@Id dışındaki tüm alanları kopyalamak için)?
  • başka bir yaklaşım var mı (klonlama çerçevesi kullanmak gibi)?
33
krisy

EntityManager.detach kullanın. Fasulyeyi artık EntityManager ile bağlantılı yapmaz. Ardından Id'yi yeni Id'ye ayarlayın (veya otomatikse null), ihtiyacınız olan alanları değiştirin ve devam edin.

43
SJuan76

EclipseLink kullanırken, ÇOK kullanışlı CopyGroup Özelliğini kullanabilirsiniz:

http://wiki.Eclipse.org/EclipseLink/Examples/JPA/AttributeGroup#CopyGroup

Büyük bir artı, çok fazla uğraşmadan, özel mülkiyete ait ilişki gemilerini de klonlamaktır.

Bu benim kodum, özel bir @ OneToMany-ilişkisine sahip bir Oynatma Listesini klonlamak birkaç satırdan ibarettir:

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

Bu yeni nesneyi kaydetmek için persist () kullandığınızdan emin olun, merge () çalışmıyor.

14
schieferstapel

Orika gibi haritalama çerçevelerini kullanabilirsiniz. http://orika-mapper.github.io/orika-docs/ Orika, verileri tekrar tekrar bir nesneden diğerine kopyalayan bir Java fasulye haritalama çerçevesidir. Yapılandırması kolaydır ve ayrıca çeşitli esneklikler sağlar.

Projemde nasıl kullandım: Bir bağımlılık ekledi:

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

Sonra aşağıdaki şekilde kodda kullanın:

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

Bu tür bir klonlamanın gerekli olduğu yerlerde birçok kullanımınız varsa, bu yardımcı olabilir.

3
Amrita

Bugün aynı sorunla karşı karşıyayım: Veritabanında bir varlık var ve istiyorum:

  • veritabanından al.
  • özelliklerinden birini değiştirin.
  • bir klonu oluşturun.
  • klonunun sadece birkaç özelliğini değiştirir.
  • veritabanında klonu devam ettirir.

Aşağıdaki adımları atmayı başardım:

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

Not : son adım (klon kalıcılığı), 'birleştirme' yerine 'persist' kullanıyorsanız, hata ayıklama modunda klon kimliğinin 'persist' komutundan sonra değiştirildiğine dikkat etsem bile çalışmıyor!

Hala karşılaştığım sorun, ayrılmadan önce ilk varlığımın değiştirilmemesi.

3
Bi30

Kabul edilen cevabın yorumlarında belirtildiği gibi, ayırma işlemi yönetilen kuruluştaki temizlenmemiş değişiklikleri görmezden gelecektir .. __ Yay kullanıyorsanız, org.springframework.beans.BeanUtils kullanmak için başka bir seçeneğiniz vardır.

Burada BeanUtils.copyProperties(Object source, Object target) var. Bu, entityManager'ı kurcalamadan sığ bir kopya yapmanızı sağlar.

Edit: Api doc'dan alıntı: "bu yöntem özelliklerin" sığ bir kopyasını "gerçekleştirmeyi amaçlıyor ve bu nedenle karmaşık özellikler (örneğin, yuvalanmış olanlar) kopyalanmayacak." 

Bu blog yazısı, Java nesnelerinin derin kopyalanması hakkında size daha fazla bilgi verebilir.

2
vertho

Bu makalede anlattığım gibi, bir kopya kurucusu kullanmaktan ve klonlanması gereken özellikleri tam olarak kontrol etmekten daha iyidir.

Öyleyse, bunun gibi bir Post varlığınız varsa:

@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 öğesini kopyalarken ve onu yenisi için şablon olarak kullanırken Post öğesini klonlamak mantıklı değildir:

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 varlığına eklemeniz gerekenler bir kopya kurucusudur:

/**
 * 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());
}

Bu, varlık klonlama/çoğaltma sorununu çözmenin en iyi yoludur. Bu işlemi tamamen otomatik hale getirmeye çalışan diğer tüm yöntemler, tüm özelliklerin kopyalanmaya değer olmadığı noktasını gözden kaçırıyor.

0
Vlad Mihalcea

Sadece kimliği boşa ayarlamayı denedim ve işe yaradı

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

kimliği null değerine ayarlamak, otomatik olarak oluşturduğum için yeni kimliğe sahip yeni bir kayda kaydedilmesini sağladı.

0
JanBrus