跳到主要内容

隐式子查询

集合 (一对多或多对多) 关联往往导致需要在查询中高频使用子查询,隐式子查询对这类子查询给予了极大的简化。

至于完整的普通子查询,请参见普通子查询

Jimmer为集合关联生成DSL的代码

以多对多关联Book.authors为例,经过编译,Jimmer会生成如下代码

BookTable.java
@GeneratedBy(type = Book.class)
public class BookTable extends AbstractTypedTable<Book> implements BookProps {

@Override
public Predicate authors(Function<AuthorTableEx, Predicate> block) {
...省略实现逻辑...
}
}

用户可以基于此authors方法构建隐含子查询。

此方法的参数是lambda表达式,其参数为关联对象的表对象,用户返回一个过滤此关联对象的SQL条件即可。

示范

BookTable table = Tables.BOOK_TABLE;

public List<Book> findBooks(@Nullable String authorName) {
return sqlClient
.createQuery(table)
.whereIf(
authorName != null && !authorName.isEmpty(),
table.authors(author -> {
return Predicate.or(
author.firstName().ilike(authorName),
author.lastName().ilike(authorName)
);
})
)
.select(table)
.execute();
}

调用上面的方法,并指定参数为非null,比如findBooks("alex"),生成的SQL如下

select
tb_1_.ID,
tb_1_.CREATED_TIME,
tb_1_.MODIFIED_TIME,
tb_1_.TENANT,
tb_1_.NAME,
tb_1_.EDITION,
tb_1_.PRICE,
tb_1_.STORE_ID
from BOOK tb_1_
where
exists(
select
1
from AUTHOR tb_2_
inner join BOOK_AUTHOR_MAPPING tb_3_
on tb_2_.ID = tb_3_.AUTHOR_ID
where
tb_3_.BOOK_ID = tb_1_.ID ➋
and
(
lower(tb_2_.FIRST_NAME) like ? /* %alex% */
or
lower(tb_2_.LAST_NAME) like ? /* %alex% */
)
)
  • ➊ 隐含子查询总是采用exists

  • ➋ Jimmmer自动生成的SQL条件,用于关联父子查询

  • ➌ ➍ 用户指定的条件,用于过滤关联对象

提示

父子查询的关联条件被自动生成,用户只需要指定关联对象过滤条件,这是隐含子查询和普通子查询的本质区别。

自动合并

动态JOIN的自动合并类似,针对相同关联多个隐含子查询也可以被自动合并。

警告

隐含子查询的合并规则并没有动态JOIN那么通用,仅限于同一个and, or或not内部

例如

BookTable table = Tables.BOOK_TABLE;

public List<Book> findBooks(
@Nullable String authorName,
@Nullable Gender authorGender
) {
return sqlClient
.createQuery(table)
.whereIf(
authorName != null && !authorName.isEmpty(),
table.authors(author -> {
return Predicate.or(
author.firstName().ilike(authorName),
author.lastName().ilike(authorName)
);
})
)
.whereIf(
authorGender != null,
table.authors(author -> author.gender().eq(authorGender))
)
.select(table)
.execute();
}

上面的例子采用了两个隐含子查询。

然而,当我们把两个参数都指定成非null时,比如*findBooks("alex", Gender.MALE)`,*最终SQL中只会出现一个子查询,如下

select
tb_1_.ID,
tb_1_.CREATED_TIME,
tb_1_.MODIFIED_TIME,
tb_1_.TENANT,
tb_1_.NAME,
tb_1_.EDITION,
tb_1_.PRICE,
tb_1_.STORE_ID
from BOOK tb_1_
where
// Merge two implicit subqueries to one real sub query
// highlight-next-line
exists(
select
1
from AUTHOR tb_2_
inner join BOOK_AUTHOR_MAPPING tb_3_
on tb_2_.ID = tb_3_.AUTHOR_ID
where
tb_3_.BOOK_ID = tb_1_.ID
and
(
lower(tb_2_.FIRST_NAME) like ? /* %alex% */
or
lower(tb_2_.LAST_NAME) like ? /* %alex% */
)
and
tb_2_.GENDER = ? /* M */
)

这是因为同一个and, or或not内部,针对相同关联的多个隐式子查询会被自动合并。