将结果集与实体映射并使用 Spring 数据进行计数的首选方式

Prefered way to map a result set with entity and count using Spring Data

经常有业务问题显示所有类别以及这些类别的使用频率。

这个问题很容易用一个查询来回答:

SELECT c.*, count(*) FROM category_assignment ca LEFT JOIN category c on ca.c_id = c.id group by c.id

我要的是您建议的基于以下内容映射结果集的方法:

@Entity
public class CategoryAssignment {
    @Id
    int id;

    @ManyToOne(fetch = FetchType.EAGER)
    private Category category;

    @ManyToOne(fetch = FetchType.EAGER)
    private Car car;
}


@Entity
public class Category {
    @Id
    String id;
    TypeEnum type;
    ...
}

@Entity
public class Car {
    @Id
    int id;
    String name;
    ...
}

从我的角度来看,映射的最佳结果是直接从存储库调用中获取一个自定义对象,其中包含作为实体的类别和作为附加变量的计数:

MappingResult result = repository.getCategoriesAndTheirCountOfType(TypeEnum type);

public class MappingResult {
    Category category;
    BigInteger count;
}

到目前为止,我能够实现它的唯一方法是手动映射结果集。但我希望有更简单的方法来映射它。

您可以使用Projections获取:

public interface CategoryAndCount {
    Category getCategory();
    Long getUsageCount();
}

public interface CategoryAssignmentRepository extends CrudRepository<CategoryAssignment, Integer> {

    @Query("select c as category, count(*) as usageCount from CategoryAssignment ca join ca.category c where c.type = ?1 group by c") 
    CategoryAndCount getCategoriesAndTheirCountOfType(TypeEnum type);
}

不要忘记在查询中为字段添加别名(c 作为 category,count(*) 作为 usageCount)。

More info

您可以通过以下两种方法之一将其作为类别的 属性。好处是 属性 始终可用,您无需调用任何特定查询。

  1. 创建数据库视图,比如 category_summary_data,然后使用辅助 table 或 @OneToOne
  2. 将其映射到类别

--

@Entity
@Table(name = "categories")
@SecondaryTable(name = "category_summary_data", pkJoinColumns = {...})
public class Category {
    @Id
    String id;
    TypeEnum type;

    @Column(name = "usage_count", table = "category_summary_data", insertable = false, updateable = false)
    // do not use int 
    Integer usageCount;
}
  1. 或者,您可以使用一些提供商扩展。 Hibernate 提供了 @Formula 注释,您可以使用它来设置计算值。

--

@Entity
@Table(name = "categories")
public class Category {
    @Id
    String id;
    TypeEnum type;

    @Formula("some sql to count the records")
    int usageCount;
}