跳到主要内容

多对一

本文通过介绍如何使用@org.babyfish.jimmer.sql.ManyToOne注解可以声明多对一关联属性

有两种方法可以实现多对一关联,基于外键和基于中间表。

1. 基于外键

Book.java
@Entity
public interface Book {

@ManyToOne
BookStore store();

...省略其他代码...
}

这里并没有配合使用@JoinColumn明确指定外键列名,Jimmer会根据命名策略推导store属性对应的列名。

如果默认的命名策略未被用户覆盖,属性store的外键列名为STORE_ID。所以,之前的代码和这里的代码等价。

Book.java
@Entity
public interface Book {

@ManyToOne
@JoinColumn(name = "STORE_ID")
BookStore store();

...省略其他代码...
}

外键可真可假。伪外键在后续文档中讨论,这里假设外键是真实的,则数据库中对应的约束为

// 如果指向关联对象的外键是真的,建立外键约束
alter table BOOK
add constraint FK_BOOK__BOOK_STORE
/* highlight-next-line */
foreign key(STORE_ID)
references BOOK_STORE(ID);

2. 基于中间表

Book.java
@Entity
public interface Book {

@Nullable
@ManyToOne
@JoinTable
BookStore store();

...
}

这里,并没有为@JoinTable指定任何属性,Jimmer会根据命名策略推导store属性对应的列名。

如果默认的命名策略未被用户覆盖,推导出的中间表信息为:

  • 中间表表名: BOOK_BOOK_STORE_MAPPING
  • 中间表指向当前实体的外键的列名: BOOK_ID
  • 中间表指向关联实体的外键的列名: STORE_ID

所以,之前的代码和这里的代码等价:

Book.java
@Entity
public interface Book {

@Null
@ManyToOne
@JoinTable(
name = "BOOK_BOOK_STORE_MAPPING",
joinColumnName = "BOOK_ID",
inverseJoinColumnName = "STORE_ID"
)
BookStore store();

...
}

中间表BOOK_BOOK_STORE_MAPPING定义如下

create table BOOK_BOOK_STORE_MAPPING(
BOOK_ID bigint not null,
STORE_ID bigint not null
);

alter table BOOK_STORE_MAPPING
add constraint PK_BOOK_STORE_MAPPING
primary(BOOK_ID, STORE_ID);

// 如果指向当前对象的外键是真的,建立外键约束
alter table BOOK_BOOK_STORE_MAPPING
add constraint FK_BOOK_BOOK_STORE_MAPPING__BOOK
foreign key(BOOK_ID)
references BOOK(ID);

// 如果指向关联对象的外键是真的,建立外键约束
alter table BOOK_BOOK_STORE_MAPPING
add constraint FK_BOOK_BOOK_STORE_MAPPING__STORE
foreign key(STORE_ID)
references BOOK_STORE(ID);

// 这个约束非常重要。
// 否则这张中间表表达的是多对多关联,而非多对一关联
alter table BOOK_BOOK_STORE_MAPPING
add constraint UQ_BOOK_BOOK_STORE_MAPPING__BOOK_ID
unique(BOOK_ID);
  • 中间表的只有两个外键,而且都非null。中间表靠插入数据和删除数据维护关联,本身从不存储null数据

  • 中间表没有对应的ORM实体,无需独立主键,两个外键联合作为主键即可

  • 默认情况下,中间表表示多对多关联。要让其退化为多对一关联,必须为指向当前对象的外键BOOK_ID指定唯一约束

警告

注意

  • 除非为了兼容已有数据库设计,多对一关联都建议直接使用外键,而非中间表

  • 一旦使用中间表映射多对一关联,Jimmer关联属性必须可null,因为数据库无法保证任何实体在中间表中一定有对应数据