spring - Criteria Api 产生了太多的连接
问题描述
实体:
@Entity
@Table(name = "shop")
public class Shop implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String address;
@OneToMany(mappedBy = "shop")
private List<Product> product = new ArrayList<>();
public Shop() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
@Entity
@Table(name = "product")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "disc_col")
public class Product implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private BigDecimal price;
@ManyToOne
private Shop shop;
public Product() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Shop getShop() {
return shop;
}
public void setShop(Shop shop) {
this.shop = shop;
}
}
@Entity
@DiscriminatorValue("loose")
public class LooseProduct extends Product {
private BigDecimal weight;
public LooseProduct() {
}
public BigDecimal getWeight() {
return weight;
}
public void setWeight(BigDecimal weight) {
this.weight = weight;
}
}
@Entity
@DiscriminatorValue("liquid")
public class LiquidProduct extends Product {
private BigDecimal volume;
public LiquidProduct() {
}
public BigDecimal getVolume() {
return volume;
}
public void setVolume(BigDecimal volume) {
this.volume = volume;
}
}
服务:
public class ShopRepositoryImpl implements ShopRepositoryCustom{
@PersistenceContext
private EntityManager em;
@Override
public List<Shop> findShops(BigDecimal volume, BigDecimal weight, BigDecimal price) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Shop> cq = cb.createQuery(Shop.class);
Root<Shop> root = cq.from(Shop.class);
Join<Shop, Product> product = root.join("product", JoinType.LEFT);
Predicate p1 = cb.equal(cb.treat(product, LiquidProduct.class).get("volume"), volume);
Predicate p2 = cb.equal(cb.treat(product, LooseProduct.class).get("weight"), weight);
Predicate p3 = cb.equal(product.get("price"), price);
cq.where(cb.and(p3, cb.or(p1, p2)));
Query q = em.createQuery(cq);
return q.getResultList();
}
}
我有一个问题,我的查询findShops
生成了太多的连接:
select shop0_.id as id1_1, shop0_.address as address2_1, shop0_.name as name3_1 from shop shop0
left outer join product product1 on shop0_.id=product1_.shop_id
left outer join product product2 on shop0_.id=product2_.shop_id
left outer join product product3_ on shop0_.id=product3_.shop_id where product3_.price=1 and (product2_.volume=1 or product3_.weight=0)
这是一种InheritanceType.SINGLE_TABLE
策略,所以它不应该创建三个连接,因为数据库中只有一个表 Product。有没有办法优化这个?
来自 org.hibernate.query.criteria.internal.CriteriaBuilderImpl 类的代码:
@SuppressWarnings("unchecked")
private <X, T, V extends T, K extends JoinImplementor> K treat(
Join<X, T> join,
Class<V> type,
BiFunction<Join<X, T>, Class<V>, K> f) {
final Set<Join<X, ?>> joins = join.getParent().getJoins();
final K treatAs = f.apply( join, type );
joins.add( treatAs );
return treatAs;
}
Treat 方法从现有连接创建新连接。每次都发生独立于继承类型。接下来休眠生成查询并且不检查连接中的重复项。当我们使用处理方法时,您知道如何防止生成额外的连接吗?
解决方案
如果您从 JPA 2.1 开始使用,您可以像这样更改查询:
select shop0_.id as id1_1, shop0_.address as address2_1, shop0_.name as name3_1
from shop shop0
left join product product1 on shop0_.id=product1_.shop_id
and (
(product1.disc_col = 'loose' and product1.weight = weight_var)
or (product1.disc_col = 'liquid' and product1.volume = volume_var)
);
对于实现,您需要将 disc_col 列的映射添加到 Product 实体。
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Shop> cq = cb.createQuery(Shop.class);
Root<Shop> root = cq.from(Shop.class);
Join<Shop, Product> product = root.join("product", JoinType.LEFT);
//add other conditions
product.on(
cb.and(
cb.or(
cb.and(cb.equal(product.get("discCol"),"liquid"),cb.equal(product.get("volume"),volumeVar)),
cb.and(cb.equal(product.get("discCol"),"loose"),cb.equal(product.get("weight"),weightVar))
)
)
);
Query q = em.createQuery(cq);
return q.getResultList();
推荐阅读
- django - 向 Django 请求添加令牌
- c++ - 如何使用 pqxx 6.4 将子复合部分作为行?
- android - 添加库以进行刀柄测试的问题
- flutter - 如何从另一个 Widget 中的一个 statefulWidget 调用方法(Flutter)
- android - 如何在 android 中从我的 Tensorflow Lite 模型传递输入并获取输出
- javascript - Typescript : 创建自定义类型 axios 实例
- javascript - 我可以在使用 Chrome 扩展程序中的 webRequest.onHeadersReceived() 保存之前修改 set-cookie 标头吗
- r - 使用 Shiny/R 渲染带有日期的数据表时出错
- matlab - 如何将封装参数传递给 Simulink 中的枚举常量?
- javascript - 无法读取未定义的属性 loadChart