直接查询中间表
被对象模型隐藏的中间表
让我们回顾一下这段实体接口定义代码
- Java
- Kotlin
@Entity
public interface Book {
@ManyToMany
@JoinTable(
name = "BOOK_AUTHOR_MAPPING",
joinColumnName = "BOOK_ID",
inverseJoinColumnName = "AUTHOR_ID"
)
List<Author> authors();
...omit other code...
}
@Entity
interface Book {
@ManyToMany
@JoinTable(
name = "BOOK_AUTHOR_MAPPING",
joinColumnName = "BOOK_ID",
inverseJoinColumnName = "AUTHOR_ID"
)
val authors: List<Author>
...omit other code...
}
上述代码中,BOOK_AUTHOR_MAPPING
表作为中间表被使用。
-
数据库的BOOK表,Java代码有与之对应的实体接口Book。
-
数据库的AUTHOR表,Java代码有与之对应的实体接口Author。
-
但是,数据库中的BOOK_AUTHOR_MAPPING表,在Java代码中没有对应的实体接口。
即,中间表被对象模型隐藏了。
直接查询中间表
Jimmer提供了一个有趣的功能,即便中间表被隐藏没有对应实体,也可以对其直接查询。
- Java
- Kotlin
AssociationTable<Book, BookTableEx, Author, AuthorTableEx> association =
AssociationTable.of(BookTableEx.class, BookTableEx::authors);
List<Association<Book, Author>> associations =
sqlClient
.createAssociationQuery(association)
.where(association.source().id().eq(3L))
.select(association)
.execute();
associations.forEach(System.out::println);
val associations = sqlClient
.queries
.forList(Book::authors) {
where(table.source.id eq 3L)
select(table)
}
.execute()
associations.forEach(::println)
这里,Java的createAssociationQuery
或Kotlin的queries.forList
表示基于中间表创建查询,而非基于实体表。
提示
这里的Java代码示范为了兼容Java8,第一行的变量association
的类型比较复杂。建议提高Java的版本,采用var
关键字。
生成的SQL如下
select
tb_1_.BOOK_ID,
tb_1_.AUTHOR_ID
/* highlight-next-line */
from BOOK_AUTHOR_MAPPING as tb_1_
where tb_1_.BOOK_ID = ? /* 3 */
果然,这是一个基于中间表的查询。
最终打印结果如下(原输出是紧凑的,为了方便阅读,这里进行了格式化):
Association{
source={
"id":3
}, target={
"id":1
}
}
Association{
source={
"id":3
},
target={
"id":2
}
}
返回数据是一系列Association对象
package org.babyfish.jimmer.sql.association;
public class Association<S, T> {
public Association(S source, T target) {
this.source = source;
this.target = target;
}
public S source() {
return source;
}
public T target() {
return target;
}
}
Association<S, T>
表示从S
类型指向T
类型关联的中间表实体。中间表实体是伪实体,没有id。它只有两个属性:
source
: 中间表指向己方的外键所对应的对象(在这个例子中,就是Book
对象)。target
: 中间表指向对方的外键所对应的对象(在这个例子中,就是Author
对象)。
备注
-
在这个例子中,并未使用对象抓取器定义Association的对象格式(事实上Association也不支持对象抓取器),因此对象的
source
和targate
关联属性仅包含对象id。 -
Author
也有一个从动的多对多关联Author.books
, 它是Book.authors
的镜像。- Java
- Kotlin
@Entity
public interface Author {
@ManyToMany(mappedBy = "authors")
List<Book> books();
...
}