SQL 使用 JPA 规范的交叉连接

SQL CROSS JOIN with JPA Specification

我有一个 Spring 引导项目,它具有三个实体 类,如下所示,

@Entity
@Table("item")
public class Item extends SomeParent {

     @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "item")
     @JsonManagedReference("product-s")
     Set<Product> products;
}

@Entity
@Table(name = "product")
public class Product extends SomeParent {

     @ManyToOne
     @JoinColumn(name = "item_id", nullable = false)
     @JsonBackReference("product-s")
     private Item item;

     @ManyToOne
     @JoinColumn(name = "fruit_id", nullable = false)
     private Fruit fruit;

     private String type;
}

@Entity
@Table(name = "fruit")
public class Fruit extends SomeParent {

     private String name;

     private Integer qty;
}

我正在使用 JPA 规范构建查询以通过 fruit.nameproduct.type 查找 items。以下是我的搜索查询,

1. fruit = mango, type = A and fruit = apple, type = B 
2. fruit = mango, type = A or fruit = apple, type = B 

通过第一个查询,它需要通过 fruit.name = mango and product.type = A fruit.name = apple and product.type = B 搜索所有 items商品必须同时包含 A 类芒果和 B 类苹果。

第二个查询需要通过fruit.name = mango and product.type = Afruit.name = apple and product.type = B搜索所有items如果任何商品包含 A 类芒果或 B 类苹果,则它应该 return item

public Specification<Item> search() {
    return (root, query, criteriaBuilder) -> {
       SetJoin<Item, Product> productJoin = root.joinSet("products", JoinType.LEFT);

       if (isAndQuery) { 
          criteriaBuilder.and(
               criteriaBuilder.and(
                    criteriaBuilder.equal(productJoin.get("fruit").get("name"), "mango"), 
                    criteriaBuilder.equal(productJoin.get("type"), "A")));
          criteriaBuilder.and(
               criteriaBuilder.and(
                    criteriaBuilder.equal(productJoin.get("fruit").get("name"), "apple"), 
                    criteriaBuilder.equal(productJoin.get("type"), "B")));
       } else {
          criteriaBuilder.or(
               criteriaBuilder.and(
                    criteriaBuilder.equal(productJoin.get("fruit").get("name"), "mango"),
                    criteriaBuilder.equal(productJoin.get("type"), "A")));
          criteriaBuilder.or(
               criteriaBuilder.and(
                    criteriaBuilder.equal(productJoin.get("fruit").get("name"), "apple"),
                    criteriaBuilder.equal(productJoin.get("type"), "B")));
       }

       return criteriaBuilder;
    };
}

我的问题是 and query 不工作,所以它没有给我结果。那么有人可以帮我解决这个问题吗?我在这里做错了什么?

这是 JPA 生成的 AND 查询,

select * from item item
left outer join product product on item.id=product.item_id 
cross join fruit fruit
where product.fruit_id=fruit.id
and ((fruit.name='mango')
and product.type='A'
and (fruit.name='apple')
and product.type='B')
order by item.id asc limit ?

这是 JPA 生成的 OR 查询,

select * from item item 
left outer join product product on item.id=product.item_id 
cross join fruit fruit
where product.fruit_id=fruit.id
and ((fruit.name='mango')
and product.type='A'
or (fruit.name='apple')
and product.type='B')
and 1=1
order by
item.id asc limit ?

AND 将不起作用,因为您正在对与其他检查相同的 product/fruit 执行过滤器检查。产品的水果不能既是苹果又是芒果。解决方案是手动创建两个单独的显式连接

public Specification<Item> search() {
   return (root, query, criteriaBuilder) -> {
       Predicate returnPredicate;
       SetJoin<Item, Product> productJoin = root.joinSet("products", JoinType.LEFT);

       if (isAndQuery) { 
          SetJoin<Item, Product> productJoin2 = root.joinSet("products", JoinType.LEFT);
          returnPredicate = criteriaBuilder.and(
               criteriaBuilder.and(
                    criteriaBuilder.equal(productJoin.get("fruit").get("name"), "mango"), 
                    criteriaBuilder.equal(productJoin.get("type"), "A"))),
               criteriaBuilder.and(
                    criteriaBuilder.equal(productJoin2.get("fruit").get("name"), "apple"), 
                    criteriaBuilder.equal(productJoin2.get("type"), "B")));
       } else {
          returnPredicate = criteriaBuilder.or(
               criteriaBuilder.and(
                    criteriaBuilder.equal(productJoin.get("fruit").get("name"), "mango"),
                    criteriaBuilder.equal(productJoin.get("type"), "A"))),
               criteriaBuilder.and(
                    criteriaBuilder.equal(productJoin.get("fruit").get("name"), "apple"),
                    criteriaBuilder.equal(productJoin.get("type"), "B")));
       }

       return returnPredicate;
   };
}

这更多地转化为给我一个既有苹果产品又有芒果产品的项目。