You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:54:08