映射是 实体关系定义 与 数据库交互 的基础,也是使用 Jimmer(ORM) 的基础。
关系映射概念
主动方与从动方
主动方:包含外键的实体,定义时关联注解 不包含 mappedBy
。
从动方:不包含外键,不能主动控制关联关系,定义时关联注解 包含 mappedBy
。
包含 mappedBy 就是从动方,作为主动方的镜像。一旦指定从动方的 mappedBy 属性,就不得在该属性上使用比如 @JoinColumn 或者 @JoinTable。
实体定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| import org.babyfish.jimmer.sql.*;
@Entity @Table(name = "app.db_student") public interface Student {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) long id();
@Key String username();
@IdView Long addressId();
@IdView @Nullable Long collegeId();
LocalDateTime birthday();
@Column("is_delete") boolean deleteFlag();
@OneToMany List<Course> courses();
@OneToOne @JoinColumn(name = "真外键名称") Address address();
@ManyToMany @JoinTable( name = "db_stu_course", joinColumns = {@JoinColumn(name = "stu_id")}, inverseJoinColumns = {@JoinColumn(name = "course_id")} ) List<Course> courses();
@ManyToOne @Nullable College college(); }
|
注解 |
说明 |
@Entity |
定义 Jimmer ORM 实体 |
@Table |
指定该实体对应的表名,也可以写入数据库的 schema,如上示例为 app schema 中的名为 db_student 的表。 |
@Id |
主键标识符,不能为 null |
@Key |
业务主键键,保存指令的根基 |
@GeneratedValue(strategy = GenerationType.IDENTITY) |
指定主键为自增策略(也可以使用自定义的算法) |
@Column |
数据库中的真实字段名 |
@JoinColumn |
覆盖默认的命名策略,指定外键名称 |
@JoinTable |
覆盖默认的命名策略,指定中间表关联和中间表的外键名称 |
必须要提的是,Java Bean 有一个不好的坏习惯,Boolean 类型且 is 开头的属性在多种序列化的工具中会自动给去掉,因此 Jimmer 中检测到 is 开头的属性会提醒你一下。
Jimmer 是支持 is 开头的属性的,但默认情况下处于关闭状态(即和 Java Bean 默认的约定一样)。可以在编译时使用 -Ajimmer.keepIsPrefix=true
属性开启。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Entity @Table(name = "db_college") public interface Address {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) long id();
@Key String province();
@Key String city();
@Key String district();
@OneToOne(mappedBy = "address") @Nullable Student student(); }
|
作为从动方的一对一关联属性必须可 null
一对一可支持双向关联,对于双向关联而言,其中一方必须主动方,另外一方为从动方。
主动方(必须):真正的数据库和关联属性之间映射,实现单向一对一关联。
从动方(可选):如果已经存在一个单向关联,可以为此配置从动方,作为主动方的镜像,形成双向关联。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Entity @Table(name = "db_college") public interface College {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) long id();
@Key String collegeName();
@OneToMany(mappedBy = "college") List<Student> students(); }
|
Jimmer 不支持单向一对多关联,一对多关联只能作为多对一关联镜像。也就是说,一对多关联必然意味着双向关联。
@OneToMany
关联仅仅是 @ManyToOne
关联的镜像,不得使用 @JoinColumn
和 @JoinTable
。
@OneToMany
修饰的关联属性必须非 null,如果查询的父对象没有子集,那么将是一个长度为 0 的空集合,而非 null。
多对一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Entity @Table(name = "db_student") public interface Student {
...
@ManyToOne @Nullable @OnDissociate(DissociateAction.DELETE) College college(); }
|
@OnDissociate 设置脱钩模式 为 DELETE,以上面的代码为例:若删除父对象 college,与之关联的子对象 students 一并删除。除此之外,还有 LAX、ChECK、SET_NULL,默认为 NONE。
多对多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Entity @Table(name = "db_course") public interface Course {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) long id();
@Key String courseName();
String courseCode();
@ManyToMany(mappedBy = "courses") List<Student> students(); }
|
多对多可支持双向关联,对于双向关联而言,其中一方必须主动方,另外一方为从动方。多对多关系中主动方和从动方可以随意抉择,二者都可以用于保存关联。
主动方(必须):真正的数据库和关联属性之间映射,实现单向多对多关联。
从动方(可选):如果已经存在一个单向关联,可以为此配置从动方,作为主动方的镜像,形成双向关联。
MappedSuperclass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
@MappedSuperclass public interface BaseEntity {
LocalDateTime createdTime();
@ManyToOne User createdBy();
LocalDateTime modifiedTime();
@ManyToOne User modifiedBy(); }
|
这样 Jimmer 的实体类就可以继承这个超类了,并且由于是接口定义的不可变实体,所以天然支持多继承。
@MapperSuperclass 的作用不仅仅是减少重复代码,还可以和其他另外两个功能配合使用:拦截器、全局过滤器