Spring Data JPA 检索最大值(Max Value)

1、简介

本文将带你了解如何使用 Spring Data JPA 检索数据列中的最大值(Max Value)。

2、示例

首先,添加 spring-boot-starter-data-jpa 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

然后,定义一个简单的 Employee 实体表示员工:

@Entity
public class Employee {
    @Id
    @GeneratedValue
    private Integer id;
    private String name;
    private Long salary; // 工资

    // 构造函数、Getter、Setter 方法省略
}

接下来,看看有哪些方法可以检索出所有员工中薪水(salary)列的最大值。

3、使用 Repository 的派生查询

Spring Data JPA 提供了一个强大的机制,可以使用 Repository 方法来定义自定义查询。其中之一是派生查询,它允许我们通过声明方法名来实现SQL查询。

创建 Employee 实体类的 Repository 接口:

public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
    Optional<Employee> findTopByOrderBySalaryDesc();
}

如上,我们实现了一个方法 findTopByOrderBySalaryDesc,该方法使用查询派生机制生成相应的 SQL。根据方法名,它将按照工资(salary)降序对所有员工进行排序,然后返回第一个员工,即工资最高的员工。

该方法会返回一个加载了所有属性的实体。如果我们只想检索一个工资(salary)值,可以使用投影查询:

创建 EmployeeSalary 投影接口:

public interface EmployeeSalary {
    Long getSalary();
}

修改 Repository 方法:

public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
    Optional<EmployeeSalary> findTopSalaryByOrderBySalaryDesc(); 
}

如果只需要检索部分列的数据,投影查询非常有用。

4、使用 JPQL 查询

另一种方法是使用 @Query 注解,直接在 Repository 接口中定义自定义 JPQL(Java Persistence Query Language)查询。

实现 JQPL 查询,以获取最高工资(salary)值:

public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
    @Query("SELECT MAX(e.salary) FROM Employee e")
    Optional<Long> findTopSalaryJQPL();
}

与上文一样,该方法会返回所有员工中最高的工资(salary)值。此外,JPQ 仅仅检索了 MAX(e.salary) 单列值,所以不需要投影查询。

5、使用原生 SQL 查询

在上例中,我们使用了 @Query 注解。这种方法还允许我们直接使用原生查询编写原始 SQL。

使用原生查询实现相同的效果:

public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
    @Query(value = "SELECT MAX(salary) FROM Employee", 
            nativeQuery = true) // 表示使用的是原生的 SQL 查询
    Optional<Long> findTopSalaryNative();
}

该解决方案与 JPQL 类似,使用原生查询可以充分利用特定的 SQL 功能或优化。

6、使用默认的 Repository 方法

还可以使用自定义 Java 代码来查询最大值。

public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
    default Optional<Long> findTopSalaryCustomMethod() {
        return findAll().stream()
          .map(Employee::getSalary)
          .max(Comparator.naturalOrder());
    }
}

如上,添加了一个新的默认方法。使用内置的 findAll() 方法检索所有 Employee 实体,然后对其进行 Stream 处理,并找出最高的工资(salary)。与以前的方法不同,所有过滤逻辑都发生在应用层,而不是数据库(如果数据量很大的话,这种方法就不适用了)。

7、使用分页

Spring Data JPA 支持分页和排序功能,可以使用它来查找员工的最高工资(salary)。

这种方式不需要实现任何自定义方法,或者是继承某个 Repository:

public Optional<Long> findTopSalary() {
    return findAll(PageRequest.of(0, 1, Sort.by(Sort.Direction.DESC, "salary")))
      .stream()
      .map(Employee::getSalary)
      .findFirst();
}

我们知道,PagingAndSortingRepository 接口为 PageableSort 类型提供了额外支持。因此,JpaRepository 中内置的 findAll() 方法也可以接受这些参数。

8、使用 Criteria API

Spring Data JPA 还提供了 Criteria API。这是一种编程式的查询构建方法,是一种更加动态和类型安全的方法,可在不使用原始 SQL 的情况下构建复杂的查询。

首先,将 EntityManager Bean 注入 Service,然后创建一个方法,使用 Criteria API 查找最高工资(salary):

@Service
public class EmployeeMaxValueService {
    @Autowired
    private EntityManager entityManager;
    
    public Optional<Long> findMaxSalaryCriteriaAPI() {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Long> query = cb.createQuery(Long.class);

        Root<Employee> root = query.from(Employee.class);
        query.select(cb.max(root.get("salary")));

        TypedQuery<Long> typedQuery = entityManager.createQuery(query);
        return Optional.ofNullable(typedQuery.getSingleResult());
    }
}

如上,首先从注入的 EntityManager Bean 获取一个 CriteriaBuilder 实例。然后,创建一个 CriteriaBuilder 来指定结果类型,并创建一个 Root 来定义 FROM 子句。最后,SELECT 工资(salary)字段的最大值并执行查询。

不过,这种方法比前一种更复杂。

9、总结

本文介绍了使用 Spring Data JPA 查找列最大值(Max Value)的各种方法,包括使用派生查询、JPQL 查询、原生 SQL 查询以及 Criteria API 查询。


Ref:https://www.baeldung.com/spring-data-jpa-max-value