首页 > 解决方案 > 如何使用 Spring Data / Hibernate 级联保持 @OneToMany 与 @EmbeddedId 的关系

问题描述

我已经看到了很多类似的问题,但还没有找到解决我所看到的问题的解决方案,所以如果这是一个多余的问题,请提前道歉。在我的情况下,我有各种类型的实体,它们每个都有自己的标签关联。所以我想要一个通用的 Tag 类,它没有自己的 id,而是由它标记的实体的 id 加上标记类型组成的 id / 复合键。为了(尝试)实现这一点,我创建了一个@Embeddableid 类:

@Embeddable
public class TagId implements Serializable {

  @Column(columnDefinition = "BINARY(16)")
  private UUID parentId;
  private String value;

  // Getters, setters...

}

该 Id 又被 a 使用@MappedSuperclass

@MappedSuperClass
public class Tag {
 
  @EmbeddedId
  private TagId id;

  // Other attributes, getters, setters...

}

...然后当我想标记特定实体时,例如使用 BookTag,该表将有一个book_id列作为 Book 表的外键来代替parentId

@Entity
@Table(name = "book_tag")
@AttributeOverride(name = "parentId", column = @Column(name = "book_id"))
public class BookTag extends Tag {

  // other attributes, getters, setters...

}

最后,我有一个 Book 实体:

@Entity
@Table(name = "book")
public class Book {

  @Id
  @GeneratedValue
  @Column(columnDefinition = "Binary(16)")
  private UUID id;

  // other attributes, getters, setters...

  @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "id.parentId")
  private List<BookTag> tags;
}

然后,当我尝试使用 Spring Data JPA 存储库来保存带有填充 BookTag 集合的新 Book 时repo.save(book),我想要的行为是保存 Book,然后将 id 复制到 BookTag 对象,然后保存这些对象。不幸的是,我在日志中看到的是 Book 按预期插入,然后运行 ​​Tag 对象的插入,但与每个条目book_id一样被绑定。null

我尝试了其他一些方法:

  1. @JoinColumn 而不是 mappedBy
  2. @MapsId 带有 @ManyToOne 对 BookTag 上的 Book 的引用
  3. @GeneratedValue 开启parentId

没有工作,但我的语法可能是关闭的。提前感谢任何知道如何解决这个问题的人。

标签: javahibernatejpaspring-data-jpahibernate-mapping

解决方案


对于任何想做类似事情的人,我终于找到了符合我标准的解决方案。

TagId 修改为:

@Embeddable
public class TagId<T> implements Serializable {

  @ManyToOne
  private T taggedEntity;
  private String value;

  // Getters, setters...

}

...这导致对 Tag 进行了轻微修改...

@MappedSuperClass
public class Tag<T> {
 
  @EmbeddedId
  private TagId id;

  // Other attributes, getters, setters...

}

...然后是 BookTag ...

@Entity
@Table(name = "book_tag")
public class BookTag extends Tag<Book> {

  // other attributes, getters, setters...

}

...最后预订:

@Entity
@Table(name = "book")
public class Book {

  @Id
  @GeneratedValue
  @Column(columnDefinition = "Binary(16)")
  private UUID id;

  // other attributes, getters, setters...

  @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "id.taggedEntity")
  private List<BookTag> tags;
}

现在我可以将 1...* BookTags 添加到 Book 中,然后我必须在所有 BookTags 上设置 Book,但随后它是对 bookRepository.save() 的一次调用,所有内容都会级联。只使用 id 会更好,但泛型足够灵活。我将让它实现一个接口,以便 toString/hashCode/equals 可以在父级上调用 getId。

唯一的另一个缺点是我无法让@AttributeOverride 工作,所以虽然我希望我的 BookTag 表有一个 book_id 列,但 tagged_entity_id 就足够了。


推荐阅读