spring-data-jpa,用于克隆 ManyToMany 映射表



我有下面的JPA实体(使用 spring-data-jpa 1.9.1.RELEASE 和 Hibernate 4.3.11.Final)

@Getter @Setter @Entity @Table(name = "product")
class Product {
  @Id @GeneratedValue
  private Long id;
  @Column(name="name")
  private String name;
  @ManyToMany(cascade = CascadeType.PERSIST)
  @JoinTable(
    name = "product_attachment",
    joinColumns = {
      @JoinColumn(name = "product_id", referencedColumnName = "id")
    },
    inverseJoinColumns = {
      @JoinColumn(name = "attachment_id", referencedColumnName = "id")
    }
  )
  private List<Attachment> attachments;
}

我需要克隆productproduct_attachment列。(不是attachment,所以是主表)

private Product _clone(Product src) {
  Product dst = new Product();
  BeanUtils.copyProperties(src, dst, "id", "attachments");
  dst.setAttachments(src.getAttachments());
  return productRepository.save(dst);
}

但我低于例外。

org.hibernate.HibernateException: Found shared references to a collection: Product.attachments

我对此问题的解决方法是再次获取相同的实体。 代码如下。

private Product _clone(Product src) {
  Product dst = new Product();
  BeanUtils.copyProperties(src, dst, "id", "attachments");
  dst.setAttachments(
    attachmentRepository.findAll(
      src.getAttachments().stream()
        .map(Attachment::getId).collect(Collectors.toList())
    )
  );
  return productRepository.save(dst);
}

但这似乎是多余的,有人知道更好的方法吗?

您不得克隆集合attachments本身,而必须复制其内容。(我认为原因是Hibernate使用了一些黑客来检测集合内容的变化)。

 dst.attachments = new ArrayList(src.attachments);

问题是您复制的列表引用了源产品(浅表)中的附件。

您应该使用与手动复制产品相同的方法复制附件条目:

Product dst = new Product();
BeanUtils.copyProperties(src, dst, "id", "attachments");
dst.setAttachments(new ArrayList<Attachment>(src.getAttachments().size()));
for(Attachment a : src.getAttachments()){
    Attachment dstA = new Attachment();
    BeanUtils.copyProperties(a, dstA, {Your properties});
    a.getAttachments().add(dstA);
}

或者,您可以使用帮助程序类(如 Apache Commons SerializationUtils.clone() 方法来执行源产品的深层副本。

最新更新