非结构化映射
非结构化设计案例
通过关联映射相关内容,不难发现,所有关联都是基于@JoinColumn
和@JoinTable
(可能被省略)。
虽然Jimmer支持伪外键 (在开发人员意识中是外键,但是数据库中并没有对应的外键约束),但是之前我们讨论的所有关联映射都是基于关系型数据库的3NF设计。
然而,你可能会接手一个遗留数据库,包含非范式化的数据库设计,例如
create table book(
id bigint unsigned not null auto_increment primary key,
// highlight-next-line
author_ids varchar(100),
...省略其他字段...
) engine=innodb;
这里,author_ids
是一个字符串列,其数据为当前Book
所关联的所有Author
对象的id用“逗号”拼接在一起,比如1, 5, 100, 206
。
很明显,这是一个非结构化设计,不满足关系型数据库的3NF。
对于支持数组类型的Postgres而言,采用数组是更好的选择,但是,为了让示范有更好的普适性,这个例子仍然采用字符串拼接,不利用 数据库产品的特有功能。
非结构化关联
为了支持这种非结构化设计,Jimmer提供了@org.babyfish.jimmer.sql.JoinSql
注解。
@JoinSql
必须也只能和@ManyToMany
配合使用
接下来,创建一个SQL函数 (这里以MySQL为例)
create function concatenation_string_contains(
self varchar(100),
id bigint
) returns boolean
begin
if self is null then
return false;
end if;
return self regexp concat('(^|\\,)\\s*', id, '\\s*(\\,|$)');
end;
该函数假设参数self
的格式是多个数字用逗号连接的结果,判断其中是否包含参数id
。例如
-
concatenation_string_contains('100, 200, 300', 200)
返回true -
concatenation_string_contains('100, 200, 300', 201)
返回false
现在,我们可以映射非结构化的多对多关联
- Java
- Kotlin
@Entity
public interface Book {
@ManyToMany
@JoinSql(
"concatenation_string_contains(" +
" %alias.author_ids, " +
" %target_alias.id" +
")"
)
List<Author> authors();
...省略其他代码...
}
@Entity
interface Book {
@ManyToMany
@JoinSql(
"concatenation_string_contains(" +
" %alias.author_ids, " +
" %target_alias.id" +
")"
)
val authors: List<Author>
...省略其他代码...
}
@JsonSql
使用的SQL表达式表示连接条件,其中
-
concatenation_string_contains
: 我们之前定义的SQL函数 -
%alias
: Jimmer约定的特殊符号,表示当前表 (在本例中为book
表) 的alias -
%target_alias
: Jimmer约定的特殊符号,表示目标表 (在本例中为author
表) 的alias
ORM自动生成SQL (这对动态查询非常重要,尤其是Jimmer这种首次提出动态表连接概念的ORM),用户无法干预最终SQL中各表的alias。
因此,在此使用%alias
和%target_alias
是唯一的办法。
值得一提的是,如果想定义双向多对多关联,由于Book.authors
已经使用了@JoinSql
,另外一端的Author.books
就没必要再次使用@JoinSql
了,和以前一样,使用mappedBy
表示自己是从动端属性即可
- Java
- Kotlin
@Entity
public interface Author {
@ManyToMany(mappedBy = "authors")
List<Book> books();
...省略其他代码...
}
@Entity
interface Author {
@ManyToMany(mappedBy = "authors")
val books: List<Book>
...省略其他代码...
}