如何使用 Spring Data JPA(Hibernate) 跨映射表过滤关联实体?
Posted
技术标签:
【中文标题】如何使用 Spring Data JPA(Hibernate) 跨映射表过滤关联实体?【英文标题】:How to filter association entities across a mapping table using Spring Data JPA(Hibernate)? 【发布时间】:2021-09-02 22:09:15 【问题描述】:有没有办法使用@OneToMany
和@ManyToOne
在中间实体(映射表)中过滤软删除的多对多关联?
product
和 product_option_group
是 N:M 关系。我正在使用disabled_datetime
列实现软删除,并希望从Product
实体中过滤ProductOptionGroup
的集合。 This post 正在使用 @ManyToMany
和 @Where
来实现这一点。我跟着它工作了(禁用product_option_group
s 从product.getProductOptionGroups()
中过滤)。请注意 @Where
上的 ProductOptionGroup
类。
// `product` <-> `product-product_option_group` <-> `product_option_group`
@Entity
@Table(name = "product")
public class Product implements Serializable
...
@ManyToMany
@JoinTable(name = "product-product_option_group",
joinColumns = @JoinColumn(name = "product_id"),
inverseJoinColumns = @JoinColumn(name = "product_option_group_id"))
private final Set<ProductOptionGroup> productOptionGroups = new HashSet<>();
...
@Entity
@Table(name = "product_option_group")
@Where(clause = "disabled_datetime is null")
public class ProductOptionGroup implements Serializable
...
@Column(name = "disabled_datetime")
private ZonedDateTime disabledDatetime;
...
但我想将@OneToMany
用于product-product_option_group
表,就像这样。
@Entity
@Table(name = "product")
public class Product implements Serializable
...
@OneToMany(mappedBy = "id.product")
private final Set<ProductProductOptionGroup> productProductOptionGroups = new HashSet<>();
...
@Entity
@Table(name = "`product-product_option_group`")
public class ProductProductOptionGroup implements Serializable
@EmbeddedId
private final ProductProductOptionGroupId id = new ProductProductOptionGroupId();
...
@Embeddable
public class ProductProductOptionGroupId implements Serializable
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "product_id", referencedColumnName = "id")
private Product product;
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "product_option_group_id", referencedColumnName = "id")
@Where(clause = "disabled_datetime is null")
private ProductOptionGroup productOptionGroup;
@Entity
@Table(name = "product_option_group")
@Where(clause = "disabled_datetime is null")
public class ProductOptionGroup implements Serializable
...
但是@Where
注释将不再起作用,因此禁用的product_option_group
s 也从product.getProductProductOptionGroups().stream().map(o -> o.getId().getProductOptionGroup().collect(Collectors.toList())
中选择。如何解决?
【问题讨论】:
【参考方案1】:我想在等待答案时添加解决方法。
首先,我可以添加一个视图作为过滤器。
CREATE VIEW `product-product_option_group-enabled` AS
SELECT *
FROM `product-product_option_group` ppog
JOIN product_option_group pog ON ppog.product_option_group_id = pog.id
AND pog.disabled_datetime IS NULL;
@Entity
@Table(name = "`product-product_option_group-enabled`") // using the view as a filter
public class ProductProductOptionGroupEnabled implements Serializable
@EmbeddedId
private final ProductProductOptionGroupId id = new ProductProductOptionGroupId();
...
然后使用ProductProductOptionGroupEnabled
进行读取和ProductProductOptionGroup
进行任何数据修改可能会解决问题。我不喜欢这个。
equals()
和 hashCode()
)。
添加视图。更改架构时最好小心。
也许我可以使用Updatable and Insertable Views,但我仍然需要ProductProductOptionGroup
来选择每一行。
其次,我可以从 Java 中过滤集合。
@Entity
@Table(name = "product")
public class Product implements Serializable
...
@OneToMany(mappedBy = "id.product")
private final Set<ProductProductOptionGroup> productProductOptionGroups = new HashSet<>();
public Set<ProductProductOptionGroup> getProductProductOptionGroups()
return productProductOptionGroups.stream()
.filter(o -> o.getId().getProductOptionGroup().getDisabledDatetime() == null)
.collect(Collectors.toSet());
public Set<ProductProductOptionGroup> getProductProductOptionGroupsAll()
return productProductOptionGroups;
...
然后使用getProductProductOptionGroups()
获取过滤后的集合。我也不喜欢这个。
disabled_datetime
是什么,它都会从product-product_option_group
表中选择每一行。
创建另一个集合。修改时一定要小心。
javax.persistence.EntityNotFoundException: Unable to find with id xxx
可能会发生。可以通过join fetch
解决麻烦的实体来解决
【讨论】:
【参考方案2】:WHERE 子句将过滤功能添加到 FROM-SELECT 结构中。在任何 JPQL 查询中,您都必须从数据库中检索选择性对象。在我看来,您应该检查@where 的正确实现,请参阅以下reference。
【讨论】:
我将org.hibernate.annotations.Where
用于@Where
注释。我找不到任何其他实现。 @Where
的正确实现是什么?
遗憾的是,org.hibernate.annotations.Where
只接受 SQL,不接受 JPQL。以上是关于如何使用 Spring Data JPA(Hibernate) 跨映射表过滤关联实体?的主要内容,如果未能解决你的问题,请参考以下文章
spring.datasource.data 拾取,但脚本从未执行
使用 spring-data-jpa 获取这些数据如何更正确?
处理 JPA 规范和 spring-data-jpa 时如何使用声明 Stream 作为返回类型
如何使用 spring4+spring-data-jpa(hibernateJpaVendorAdapter)+multidatasource+one entityManager+jpaReposit