Spring Data JPA在空结果集上聚合函数

时间:2018-09-16 20:29:26

标签: java spring spring-data-jpa spring-data h2

我正在从事一个涉及SpringJPA/Hibernate的项目。我的开发环境中使用的数据库驱动程序是H2。我的应用程序有一个显示统计信息的页面,这种统计信息的一个示例是用户的平均年龄。但是,当我尝试使用JPQL获取平均年龄时,会收到异常

Result must not be null!

为简单起见,假设我将年龄作为integer存储在每个User对象上(在我的应用程序中当然不是这样,但这对我的问题并不重要)。

用户模型

@Entity
public class User implements Identifiable<Long> {
    private int age;
    // more fields and methods, irrelevant
}

用户存储库

@Repository
public interface UserRepository extends CrudRepository<User, Long> {
    @Query("SELECT AVG(u.age) FROM #{#entityName} u")
    long averageAge();
}

我似乎无法弄清楚为什么调用UserRepository#averageAge();会引发异常。我尝试将查询中的函数AVG替换为COUNT,这的行为符合预期。我也尝试过使用SQL查询并在注释中设置nativeQuery = true,但无济于事。我当然可以通过获取所有用户并计算纯Java语言的平均年龄来解决此问题,但这并不是很有效。

堆栈跟踪:

Caused by: org.springframework.dao.EmptyResultDataAccessException: Result must not be null!
    at org.springframework.data.repository.core.support.MethodInvocationValidator.invoke(MethodInvocationValidator.java:102)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy150.averageAge(Unknown Source)
    at my.test.application.StatisticsRunner.run(StatisticsRunner.java:72)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:809)
    ... 30 more

已解决

该异常是由以下事实引起的:AVG()在空表上执行时返回null。我通过修改查询(受the answer to this question启发)来修复它,如下所示:

@Query("SELECT coalesce(AVG(u.age), 0) FROM #{#entityName} u")
long averageAge();

3 个答案:

答案 0 :(得分:2)

当查询的结果预期至少包含一行(或元素)但没有返回任何行时,似乎抛出了EmptyResultDataAccessException异常。

与此相关的文档可以在here中找到。

我建议运行与尝试运行相同的查询,以进一步验证该理论。现在好的问题是如何处理。

您有两个选择。要么在您的调用点捕获EmptyResultDataAccessException异常,然后在那里直接处理它,要么可以选择一个ExceptionHandler来处理此类异常。

两种处理方式都应该可以,您可以根据自己的情况在每种方式之间进行选择。

答案 1 :(得分:1)

如果使用Spring Data,并且在Hibernate找不到匹配项时方法返回null,请确保将@org.springframework.lang.Nullable添加到方法签名中:

public interface SomeRepositoryCustom {
    @org.springframework.lang.Nullable
    public Thing findOneThingByAttr(Attribute attr) {
        /* ...your logic here... */
    }
}

这是因为Spring Data检查了方法的可为空性,并且如果缺少注释,它将强制您始终需要返回一个对象:

/* org.springframework.data.repository.core.support.MethodInvocationValidator */

@Nullable
@Override
public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) throws Throwable {
    /* ...snip... */

    if (result == null && !nullability.isNullableReturn()) {
        throw new EmptyResultDataAccessException("Result must not be null!", 1);
    }

    /* ...snip... */

我使用了Spring Boot版本2.1.1.RELEASE和Spring Data 2.1.4.RELEASE

答案 2 :(得分:0)

我不太确定,但是我认为问题是因为返回long的类型,也许您应该使用Long包装器,因为它是原始类型,所以long不允许null,请尝试将其更改为

$(".row[data-id='1']"); //returns nothing

$(".row").data("id"); //returns 0 (expected)