在JpaRepository中使用多字段FindAllBy查询返回空的问题排查与解决
嘿,我来帮你理清问题所在,以及实现你想要的多字段成对匹配查询的正确方式~
问题根源
你误解了Spring Data JPA命名查询的逻辑!你的findAllByFooAndBar方法接收两个Iterable参数时,生成的SQL是这样的:
SELECT t FROM Thing t WHERE t.foo IN ?1 AND t.bar IN ?2
这个查询的意思是:所有foo值在第一个列表里,并且bar值在第二个列表里的实体——这会命中你保存的全部3个实体(因为(1,4)的foo在[1,3]、bar在[2,4]),但你的测试里返回0可能是其他小问题(比如事务或参数处理的小意外),不过这不是重点,重点是这个逻辑完全不符合你想要的「匹配(1,2)和(3,4)这两组成对字段」的需求。
正确解决方案
根据你的需求,下面给你两种靠谱的实现方式:
方案1:自定义JPQL(简洁高效,推荐)
利用JPA的行值表达式语法,直接匹配成对的(foo, bar)组合,你的H2配置已经开启了PostgreSQL模式,完全支持这个语法:
package com.mytest.findallbytest.repository import com.mytest.findallbytest.model.Thing import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query import org.springframework.data.repository.query.Param interface ThingRepository : JpaRepository<Thing, Long> { @Query("SELECT t FROM Thing t WHERE (t.foo, t.bar) IN :pairs") fun findAllByFooBarPairs(@Param("pairs") pairs: List<Pair<Long, Long>>): Iterable<Thing> }
然后在测试里这样调用:
// 直接传入成对的参数组合 assertThat(repo.findAllByFooBarPairs(listOf(1L to 2L, 3L to 4L))).hasSize(2)
方案2:使用Specification(灵活通用)
如果之后需要更动态的查询条件,用Specification是更好的选择,兼容性也更强:
首先修改Repository继承JpaSpecificationExecutor:
package com.mytest.findallbytest.repository import com.mytest.findallbytest.model.Thing import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaSpecificationExecutor interface ThingRepository : JpaRepository<Thing, Long>, JpaSpecificationExecutor<Thing> { }
然后在测试里构建查询条件:
@Test fun `basic entity checks`() { repo.save(Thing(1, 2)) repo.save(Thing(3, 4)) repo.save(Thing(1, 4)) assertThat(repo.findAll()).hasSize(3) // 构建成对匹配的查询条件 val targetPairs = listOf(1L to 2L, 3L to 4L) val spec = Specification<Thing> { root, _, cb -> val predicates = targetPairs.map { (foo, bar) -> cb.and(cb.equal(root.get<Long>("foo"), foo), cb.equal(root.get<Long>("bar"), bar)) } cb.or(*predicates.toTypedArray()) } assertThat(repo.findAll(spec)).hasSize(2) }
补充说明
如果你好奇为什么原来的方法返回空:
你的测试里repo.findAll()能返回3个实体,说明save操作没问题,那findAllByFooAndBar返回空大概率是Spring Data JPA对Iterable参数的处理和你预期不符——不过这个方法本身逻辑就不符合你的需求,所以直接换成上面的方案就好啦。
内容的提问来源于stack exchange,提问作者Tobias Hermann
相关产品推荐
相关产品推荐

