spring-boot - JPQL 如何总结子节点的给定属性并获取这些子节点以避免 n + 1 个选择?

标签 spring-boot jpa jpql

我有两个实体:类别和产品。它们是关联的,类别是父级:

@Entity
@Table(name = "categories")
public class Category {
    @Id
    @GeneratedValue(generator = "inc")
    @GenericGenerator(name = "inc", strategy = "increment")
    private int id;
    private String name;
    private int totalQuantity;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "category")
    private Set<Product> products;
  
    public Category(int id, String name, int totalQuantity, Set<Product> products) {
        this.id = id;
        this.name = name;
        this.totalQuantity = totalQuantity;
        this.products = products;
    }

产品实体:

@Entity
@Table(name = "products")
public class Product {
    @Id
    @GeneratedValue(generator = "inc")
    @GenericGenerator(name = "inc", strategy = "increment")
    private int id;
    private String name;
    private int amount;
    @ManyToOne
    @JoinColumn(name = "category_id")
    private Category category
}

(totalQuantity 是与该类别关联的产品的数量总和) 我想以防止 n + 1 的方式获取所有类别和所有关联产品并进行求和。这是我的查询是错误/未完成的,因为我不知道如何执行/完成它:

@Query("SELECT new com.example.demo.category.Category(p.category.id, p.category.name, SUM(p.amount), ) FROM Product p GROUP BY p.category.id")
List<Category> findAll();

编辑: 为了更好地展示目标,我添加了我的类别 View (“前端”、“后端”)以及与每个类别相关的产品: Category view

最佳答案

使用@Formula关键字执行原生sql来计算产品金额的总和。

// the p.category_id=id <-- this id is Category itself id
@Formula("(SELECT COALESCE(SUM(p.amount),0) FROM products p INNER JOIN categories c ON p.category_id=c.id WHERE p.category_id=id)")
private int totalQuantity;

Note: If your totalQuantity type is int, you need to use COALESCE avoid the null value. If it type is Integer, you don't need to use it.

使用LEFT JOIN FETCH来防止N+1问题。

@Query("SELECT DISTINCT c FROM Category c LEFT JOIN FETCH c.products")
List<Category> findAll()

Hibernate sql 结果:

select distinct category0_.id                         as id1_6_0_,
                products1_.id                         as id1_11_1_,
                category0_.name                       as name2_6_0_,
                (SELECT COALESCE(SUM(p.amount), 0)
                 FROM product p
                          INNER JOIN categories c ON p.category_id = c.id
                 where p.category_id = category0_.id) as formula1_0_,
                products1_.amount                     as amount2_11_1_,
                products1_.category_id                as category4_11_1_,
                products1_.name                       as name3_11_1_,
                products1_.category_id                as category4_11_0__,
                products1_.id                         as id1_11_0__
from categories category0_
         left outer join products products1_ on category0_.id = products1_.category_id

您可以使用一个查询同时获取所有类别和产品的总数量。


旧答案

您可以使用LEFT JOIN FETCH来防止n+1问题。

@Query("SELECT DISTINCT c FROM Category c LEFT JOIN FETCH c.products")
List<Category> findAll();

使用sum()运算符计算产品数量。

List<Category> categories = categoryRepository.findAll().stream().peek(category -> {
    Set<Product> products = category.getProducts();
    if (!ObjectUtils.isEmpty(products)) {
        int totalQuantity = products.stream().mapToInt(Product::getAmount).sum();
        category.setTotalQuantity(totalQuantity);
    }
}).collect(Collectors.toList());

最后,您可以使用一个查询来获取所有类别并统计产品总数。


关于spring-boot - JPQL 如何总结子节点的给定属性并获取这些子节点以避免 n + 1 个选择?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68201892/

相关文章:

java - 将json数据ajax传递给Spring boot

java - Spring 启动/MongoDB : expecting to have different URL for GET and DELETE

spring-boot - 将 springBootTest 注释与 WebEnvironment 一起使用时出现 NullPointerException

java - 2个表选择什么关系

java - 在 Hibernate 中通过映射表值排序

java - 标准 API : filter by class type

spring-boot - 如何排除从项目依赖项中扫描的 Spring bean?

java - 使用 Eclipselink 和 Moxy 将 List<someObject> 写入文件

java - JPA查询左连接而不加载连接实体字段

mysql - 用于检索按日期 desc 排序的单个记录的 SQL 和/或 JPQL 语句