如果连接列不包含某个值,JPA 标准构建器仅返回实体

Posted

技术标签:

【中文标题】如果连接列不包含某个值,JPA 标准构建器仅返回实体【英文标题】:JPA criteria builder only return entity if join column does not contain a certain value 【发布时间】:2021-06-17 13:37:32 【问题描述】:

我有两个班:

@Entity
@Table(name = "foo")
data class Foo 
  @Id
  val id: UUID
  // a lot more values

  @ElementCollection(fetch = FetchType.EAGER)
  @CollectionTable(name = "foo_bar", joinColumns = [JoinColumn(name = "foo_bar_id")])
  val bars: Set<FooBar>?


data class FooBar 
  val fooBarId: UUID
  val bar: Bar
  val validUntil: Instant

目前,我使用 Predicate 和 CriteriaBuilder 在此实体上拥有广泛的搜索功能。我希望能够在我们的搜索中仅包含 Foo 对象,这些对象在其 FooBar JoinColumn 行中具有特定 Bar 值,并且如果 none 的 joinColumn 行具有该特定 Bar 则仅返回 FooBar 对象的函数价值。

我现在拥有的是:



    fun searchPaged(
        // numerous criteria values
        flipFilterOnBar: Boolean?,
        bar: Bar?,
        pageable: Pageable
    ): Page<Foo> 
  return findAll( root, _, cb ->
    
    val predicates = mutableListOf<Predicate>(
                cb.or(
                   //some criteria
                )
            )

     //more criteria
     //
     //

      if (bar != null) 
                val joinColumn: Join<Foo, FooBar> = root.join<Foo?, FooBar?>("bars", JoinType.INNER)
                if (flipFilterOnBar == true)  //Nullable boolean
                    
                    // this part makes the query return a row for each join column and filters out only the filtered value (returning more rows than if this clauses was not added.
                    predicates.add(
                    cb.notEqual(joinColumn.get<Bar>("bar"), bar)
                    )
                 else 
                    predicates.add(
                        cb.equal(joinColumn.get<Bar>("bar"), bar)
                    )
                
            

            cb.and(*predicates.toTypedArray())
        , pageable)

我想知道如何在此处使用标准构建器仅在 FooBar 行都不包含指定的 Bar 值时返回 Bar 实体。我现在编写它的方式将所有 Foo 和 FooBar 行连接在一起,并且只过滤具有指定 Bar 值的组合行,返回另一个不是我想要的组合行。

例如,如果我有一个包含 3 个 FooBar 的 Foo,并且我想过滤掉所有 Foo 的 Bar 值为“b”。查询检索此 Foo 3 次(每个 Bar 元素一次),并且仅过滤其中包含 Foo 且值为“b”的组合行。返回其他两行。我希望查询不返回任何这些行,因为其中一行包含值“b”。

【问题讨论】:

【参考方案1】:

尝试使用类似于所有 foos 的谓词,这些 foos 传递了前面的谓词,并且其 id 不在 foos 的 id 集合中,而 foobars 的 bar 值为“b”。

        Subquery<UUID> subquery = criteriaQuery.subquery(UUID.class);
        Root<Foo> fooSubqueryRoot = subquery.from(Foo.class);
        Join<Foo, FooBar> fooBarSubqueryJoin = fooSubqueryRoot.join("bars");
        Join<FooBar, Bar> barSubqueryJoin = fooBarSubqueryJoin.join("bar");
        subquery.where(cb.equal(barSubqueryJoin.get("value"), "b"));
        //foos ids of foos with foobars whose bar value is "b"
        subquery.distinct(true).select(fooSubqueryRoot.get("id"));
        //exclude foos with foobars whose bar value is "b"
        predicates.add(root.get("id").in(subquery).not());

【讨论】:

以上是关于如果连接列不包含某个值,JPA 标准构建器仅返回实体的主要内容,如果未能解决你的问题,请参考以下文章

postgresql中的Rails json列不接受任何值,并在存储后调用时返回nil

Spring JPA 标准构建器大于:我可以传递字符串值以比较数据库中的数字类型吗

函数一(标准库函数,自定义函数)

Spring Data JPA 查询从一个连接表中选择所有值

Spring Data JPA 查询不起作用,列不存在

学习笔记JPA