使用DATEDIFF函数的JPQL查询导致JUnit测试失败
我之前也碰到过类似的问题,其实根源就在于H2数据库和MySQL对DATEDIFF函数的语法要求不一样,导致你的JPQL查询在生产环境(MySQL)能正常运行,但在测试环境(H2)就触发了参数类型错误,进而间接引发了Autowired相关的上下文加载异常。
问题原因拆解
MySQL的DATEDIFF只需要两个参数:DATEDIFF(结束日期, 开始日期),返回两个日期之间的天数差;但H2的DATEDIFF要求三个参数:DATEDIFF(时间单位, 开始日期, 结束日期),比如DATEDIFF(DAY, o.deleted, CURRENT_DATE)。你的JPQL用了MySQL的写法,H2解析时会认为参数数量和类型都不符合要求,所以才抛出FunctionArgumentException。
解决方案
最省心的办法是让H2模拟MySQL的行为,这样你的JPQL查询不用改就能兼容两种数据库:
修改测试环境的H2数据源配置
在你的测试配置文件(比如application-test.properties)里,给H2的URL加上MODE=MySQL参数:spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE这个配置会让H2切换到MySQL兼容模式,自动识别MySQL风格的
DATEDIFF语法,完全不用修改你的JPQL业务代码。(备选)修改JPQL查询为兼容写法
如果不想调整H2配置,也可以用JPA的FUNCTION函数适配不同数据库:@Query("SELECT o FROM OutputStreamJpa o WHERE o.deleted IS NOT NULL " + "AND FUNCTION('DATEDIFF', CURRENT_DATE, o.deleted) >= :days") List<OutputStreamJpa> findDeletedOlderThanFixedDays(Integer days);不过这种写法虽然通用,但不如第一种方案省心,毕竟不用改动业务代码。
验证
改完配置后重新运行JUnit测试,Hibernate就能正常初始化查询,Autowired相关的间接错误也会随之消失。
备注:内容来源于stack exchange,提问作者boh

