it-swarm.com.ru

Клонирование объекта JPA

У меня уже есть сущность JPA в базе данных.
Я хотел бы получить его копию (с другим идентификатором) с некоторыми измененными полями.

Какой самый простой способ сделать это? Подобно:

  • установить в поле @Id значение null и сохранить его будет работать?
  • придется ли мне создавать метод клонирования для объекта (копировать все поля, кроме @Id)?
  • есть ли другой подход (например, использование клонирования)?
33
krisy

Используйте EntityManager.detach. Это делает бин больше не связанным с EntityManager. Затем установите для Id новый Id (или ноль, если он автоматический), измените нужные поля и сохраните их.

43
SJuan76

При использовании EclipseLink вы можете использовать ОЧЕНЬ удобную функцию CopyGroup:

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

Большим плюсом является то, что без особых усилий он клонирует и частные отношения.

Это мой код, клонирование Playlist с его частными @ 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 () не работает.

14
schieferstapel

Вы можете использовать картографические рамки, такие как Orika. http://orika-mapper.github.io/orika-docs/ Orika - это среда отображения bean-компонентов Java, которая рекурсивно копирует данные из одного объекта в другой. Он прост в настройке и предоставляет различные гибкие возможности.

Вот как я использовал его в своем проекте: Добавил зависимость:

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

Это может помочь, если у вас есть много случаев, когда требуется такое клонирование.

3
Amrita

Сегодня я сталкиваюсь с той же проблемой: у меня есть объект в базе данных, и я хочу:

  • получить его из базы данных
  • изменить значение одного из его атрибутов
  • создать его клон
  • изменить только несколько атрибутов клона
  • сохранить клон в базе данных

Мне удалось сделать следующие шаги:

@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», даже если я отмечаю в режиме отладки, что идентификатор клона был изменен после команды «persist»!

Проблема, с которой я все еще сталкиваюсь, заключается в том, что моя первая сущность не была изменена до того, как я ее отсоединил.

3
Bi30

Как упомянуто в комментариях к принятому ответу, detatch будет игнорировать неосуществленные изменения в управляемом объекте Если вы используете spring, у вас есть другая опция, которая заключается в использовании org.springframework.beans.BeanUtils

Здесь у вас есть BeanUtils.copyProperties(Object source, Object target). Это позволит вам сделать поверхностную копию без вмешательства в entityManager.

Edit: Цитата из api doc: «этот метод предназначен для выполнения« поверхностной копии »свойств, поэтому сложные свойства (например, вложенные) не будут скопированы». 

Это сообщение в блоге может рассказать вам больше о глубоком копировании объектов Java.

2
vertho

Как я объяснил в этой статье , вам лучше использовать конструктор копирования и точно контролировать, какие атрибуты необходимо клонировать.

Итак, если у вас есть объект 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());
}

Это лучший способ решения проблемы клонирования/дублирования объектов. Любые другие методы, которые пытаются сделать этот процесс полностью автоматическим, упускают из виду тот факт, что не все атрибуты заслуживают дублирования.

0
Vlad Mihalcea