Подтвердить что ты не робот

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

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

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

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

Ответ 1

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

Ответ 2

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

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() не работает.

Ответ 3

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

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

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

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

Примечание: последний шаг (clone persistence) не работает, если я использую 'persist' вместо 'merge', даже если я отмечаю в режиме отладки, что идентификатор клонирования был изменен после 'persist' команда!

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

Ответ 4

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

Вот как я использовал его в своем проекте: добавил 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);

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

Ответ 5

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

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

Редактировать: цитата из api doc: "этот метод предназначен для выполнения" поверхностной копии "свойств, поэтому сложные свойства (например, вложенные) не будут скопированы".

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

Ответ 6

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

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

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

Ответ 7

Я просто попытался установить идентификатор на ноль, и это сработало

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

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