我可以使用JpaSpecificationExecutor使带子查询的操作符与带有相同表的子查询分组吗?

时间:2019-10-18 03:10:07

标签: spring-data-jpa specifications

我正在使用Spring Data的存储库实现JpaSpecificationExecutor。 我可以使用JapSpecificationExecutor的findAll方法的Specification创建动态的where子句。

这样的当前代码和表架构

历史记录表

id |留言| groupId

1'text'1

2个“内容” 1

3'ttt'2

4'rrr'3

historyRepository
                         .findAll(
                                 where(
                                         /** common */
                                         sndId(sndUId))
                                         .and(kind(kind))
                                         .and(sndIdNotNull())
                                         .and(groupTokenNotNull())
                                         /** search option */
                                         .and(searchHistory(historyRequestDto))
                                 /** order by date */
                                 , new PageRequest(0, 50, new Sort(Sort.Direction.DESC, "sndDate"))
                         )

我想让我的代码生成这样的本地mysql查询

select * from History 
where
    id IN (
        select 
            min(id) 
        from 
            History 
        where
            kind = 4 
            and kind is not null
            and sndId is not null
            and groupToken is not null
        group by (groupToken) 
) order by sndDate desc
limit 0, 10    

我可以生成类似的代码吗?

2 个答案:

答案 0 :(得分:0)

是的。

findAll方法需要一个Specification,它基本上是来自的功能

RootCriteriaQueryCriteriaBuilderPredicate

https://thoughts-on-java.org/hibernate-tip-subquery-criteriaquery/描述了如何创建x in (subquery)条件:

类似的事情应该起作用:

(Root root, CriteriaQuery cq, CriteriaBuilder cb) -> {
    Subquery sub = cq.subquery(Long.class);
    Root subRoot = sub.from(History.class);
    sub.select(cb.min(subRoot.get("id")));
    sub.where(/* insert your existing conditions here */);

    // check the result of the subquery
    return cb.in(sub);
}

我实际上没有尝试过,因此当然需要进行一些调整,但是总体思路应该可行。

答案 1 :(得分:0)

这样的事情应该对我有用:

@RunWith(AndroidJUnit4::class)
class SomeActivityInstrumentedTest{

    @get: Rule
    public var rule: ActivityScenarioRule<*> = ActivityScenarioRule(SomeActivity::class.java)

    @Test
    fun launchActivity() {
        try {
            val scenario = rule.scenario as ActivityScenario<SomeActivity>
            scenario.moveToState(Lifecycle.State.CREATED)
            scenario.moveToState(Lifecycle.State.STARTED)
            scenario.moveToState(Lifecycle.State.RESUMED)
            Assert.fail("should have failed earlier because id is not set")
        } catch (e: Error) {
            assertEquals(e.message, "id is null")
        }
    }
}