Skip to main content

4.4 Dynamic Table Join

Usage

@Nullable String storeName = ...Omitted...;
@Nullable String storeWebsite = ...Omitted...;

BookTable table = BookTable.$;
List<Book> books = sqlClient
.createQuery(table)
.where(table.store().name().eqIf(storeName))
.where(table.store().website().eqIf(storeWebsite))
.select(table)
.execute();
info

In the above code, Java's table.store() and Kotlin's table.store represent the inner join between the table defined by Book and the table defined by BookStore through the many-to-one association Book.store.

That is, the following SQL logic is represented:

from BOOK b inner join BOOK_STORE s on b.STORE_ID = s.ID

In fact, if the entity model is richer, longer paths can be written, such as table.store().city().province().

Here, just for quick start guide and preview, it is unnecessary to build a richer entity model to demonstrate longer paths. The shortest table chain join path table.store() is enough.

Various Situations

All table joins do not take effect

If both storeName and storeWebsite are null, it will cause the dynamic table joins created at ❶ and ❷ to be unused. Unused table joins will be automatically ignored without rendering any actual SQL joins.

At this time, the following SQL is generated:

select
tb_1_.ID,
tb_1_.NAME,
tb_1_.EDITION,
tb_1_.PRICE,
tb_1_.STORE_ID
from BOOK tb_1_

Some table joins take effect

If storeName is specified as non-null while storeWebsite remains null, the dynamic table join at ❶ takes effect, while the dynamic table join at ❷ is ignored.

At this time, the following SQL is generated:

select
tb_1_.ID,
tb_1_.NAME,
tb_1_.EDITION,
tb_1_.PRICE,
tb_1_.STORE_ID
from BOOK tb_1_
inner join BOOK_STORE tb_2_
on tb_1_.STORE_ID = tb_2_.ID
where
tb_2_.NAME = ? /* MANNING */

All table joins take effect

If both storeName and storeWebsite are specified as non-null, the dynamic table joins created at ❶ and ❷ will both take effect.

tip

Jimmer can automatically merge conflicting table joins. The two joins will be merged into one. Ultimately, only one SQL join will be rendered

select
tb_1_.ID,
tb_1_.NAME,
tb_1_.EDITION,
tb_1_.PRICE,
tb_1_.STORE_ID
from BOOK tb_1_
inner join BOOK_STORE tb_2_
on tb_1_.STORE_ID = tb_2_.ID
where
tb_2_.NAME = ? /* MANNING */
and
tb_2_.WEBSITE = ? /* https://www.manning.com */

IsNull和外连接

类似Java中table.store()或Kotlintable.store这样的连接路径,会被渲染成SQL的内连接,而非外连接,这是因为

info

内连接比外连接拥有更好的性能!

caution

为此,Jimmer不惜通过异常让默认情况下内连接而得到的表对象不支持isNull

如果要对关联对象施加isNull,必须明确采用外连接操作,例如

TreeNodeTable table = TreeNodeTable.$;
List<TreeNode> rootNodes = sqlClient
.createQuery(table)
.where(table.parent(JoinType.LEFT).isNull())
.select(table)
.execute();

Java代码中的.parent(JoinType.LEFT)和Kotlin代码中的.parent?表示左连接。

其实,这种案例更适合上一文中的关联id条件

TreeNodeTable table = TreeNodeTable.$;
List<TreeNode> rootNodes = sqlClient
.createQuery(table)
.where(table.parentId().isNull())
.select(table)
.execute();