如何使用 Spring Data / Hibernate 级联保持 @OneToMany 与 @EmbeddedId 的关系

Posted

技术标签:

【中文标题】如何使用 Spring Data / Hibernate 级联保持 @OneToMany 与 @EmbeddedId 的关系【英文标题】:How do I cascade persist an @OneToMany relationship with an @EmbeddedId using Spring Data / Hibernate 【发布时间】:2021-12-27 12:35:17 【问题描述】:

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

@Embeddable
public class TagId implements Serializable 

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

  // Getters, setters...


该 ID 又被 @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

我尝试了其他几种方法:

    @JoinColumn 而不是 mappedBy @MapsId 带有 @ManyToOne 对 BookTag 上的 Book 的引用 @GeneratedValue on parentId

没有任何效果,但我的语法可能已关闭。提前感谢任何知道如何解决此问题的人。

【问题讨论】:

【参考方案1】:

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

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 就足够了。

【讨论】:

以上是关于如何使用 Spring Data / Hibernate 级联保持 @OneToMany 与 @EmbeddedId 的关系的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spring data mongo 和 Spring data elasticsearch 时如何建模?

如何将 @Transactional 与 Spring Data 一起使用?

“ java.lang.NoClassDefFoundError:javax / persistence / criteria / Selection”在JavaEE Maven项目中使用Hibern

使用 spring-data-jpa 获取这些数据如何更正确?

如何在 Spring Boot 应用程序的同一个域类上同时使用 Spring Data JPA 和 Spring Data Elasticsearch 存储库?

如何在 spring-boot 中禁用 spring-data-mongodb 自动配置