4.4 Dynamic Table Join
Usage
- Java
- Kotlin
@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();
val storeName: String? = ...Omitted...
val storeWebsite: String? = ...Omitted...
val books = sqlClient
.createQuery(Book::class) {
where(table.store.name `eq?` storeName) ❶
where(table.store.website `eq?` storeWebsite) ❷
select(table)
}
.execute()
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.
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的内连接,而非外连接,这是因为
内连接比外连接拥有更好的性能!
为此,Jimmer不惜通过异常让默认情况下内连接而得到的表对象不支持isNull
。
如果要对关联对象施 加isNull
,必须明确采用外连接操作,例如
- Java
- Kotlin
TreeNodeTable table = TreeNodeTable.$;
List<TreeNode> rootNodes = sqlClient
.createQuery(table)
.where(table.parent(JoinType.LEFT).isNull())
.select(table)
.execute();
val rootNodes = sqlClient
.createQuery(TreeNode::class) {
where(table.`parent?`.isNull())
select(table)
}
.execute()
Java代码中的.parent(JoinType.LEFT)
和Kotlin代码中的.parent?
表示左连接。
其实,这种案例更适合上一文中的关联id条件
- Java
- Kotlin
TreeNodeTable table = TreeNodeTable.$;
List<TreeNode> rootNodes = sqlClient
.createQuery(table)
.where(table.parentId().isNull())
.select(table)
.execute();
val rootNodes = sqlClient
.createQuery(TreeNode::class) {
where(table.parentId.isNull())
select(table)
}
.execute()