# 实体关系
实体关系可用于数据访问中的关联查询(joins)和展开查询(expand),如在关联查询时只需指定关系名,便能在生成SQL中根据关系的目标实体及关联字段,拼接生成对应的表名、及列。目前支持定义的关系有:多对一关系,一对多关系。
关系注解映射类见:
fly.data.relational.mapping.forclass.SimpleRelationMapper关系处理类见:
fly.data.relational.mapping.processor.RelationMappingProcessor
# 多对一关系
在实体类上或实体字段上使用@ManyToOne注解,定义多对一关系。如成员与组织为两个实体之间的多对一关系,子组织与父组织为同一个实体的多对一关系。
- 作用于实体类,需要明确关联的字段及关系名
import fly.core.data.annotation.JoinField;
import fly.core.data.annotation.ManyToOne;
@Entity
@ManyToOne(targetEntity = Org.class, optional = true, relation = "parent", fields = {
@JoinField(name = "parentId", referencedFieldName = "id")
})
public class Org {
@UUID
protected String id;
@Column
protected String parentId;
// get/set
}
- 作用于实体字段,定义更加简洁
import fly.core.data.annotation.ManyToOne;
@Entity
public class Org {
@UUID
protected String id;
// 默认关联到目标实体的主键字段,即 parentId -> id
// 默认关系名根据字段会解析为 parent
@ManyToOne(targetEntity = Org.class, optional = true)
protected String parentId;
// get/set
}
@ManyToOne注解属性简单说明:
| 注解属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
targetEntity | 必填,等同于注解的value,定义关系目标实体类 | Class<?> | - |
relation | 关系名 | String | 1. 注解作用于实体类时,默认关系名为目标实体名的首字母小写;2. 若注解作用于字段,如在authorId字段上添加关系,关联字段为Author实体的id,则关系名为author。其他情况则为字段名,关系名不建议与字段名重复,此时应自定义关系名 |
fields | 关联字段 | JoinField[] | 若注解作用于字段,默认为当前字段关联目标实体的主键 |
optional | 关系是否可选,影响关联查询时使用LEFT JOIN(true)还是JOIN | boolean | true |
expandable | 该关系是否可展开查询 | boolean | true |
defaultExpanding | 展开查询时默认返回的字段 | String[] | 关系实体所有可查询字段 |
generateForeignKey | ddlAuto时是否产生外键约束 | boolean | true |
foreignKeyName | ddlAuto时产生的外键约束名称 | String | fk_{entityName}_{relationName} |
# 一对多关系
在实体类上添加@OneToMany注解定义一对多关系,定义前需要目标实体先存在与本实体的多对一关系。
下示例中,子组织对父组织为多对一关系,已在parentId字段上定义;而父组织对子组织为一对多关系,在实体类上定义:
import fly.core.data.annotation.OneToMany;
@Entity
@OneToMany(name = "children", targetEntity = Org.class, defaultExpanding = {"name"})
public class Org {
@UUID
protected String id;
@ManyToOne(targetEntity = Org.class, optional = true)
protected String parentId;
// get/set
}
@OneToMany注解属性简单说明:
| 注解属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
name | 必填,关系名 | String | - |
targetEntity | 必填,关系目标实体类 | Class<?> | - |
inverseRelation | 若目标实体类有多个关联到本实体的多对一关系,则需要用该属性明确指定是用哪个多对一关系 | String | 默认取目标实体关联到本实体的唯一一个多对一关系 |
expandable | 该关系是否可展开查询 | boolean | true |
defaultExpanding | 展开查询时默认返回的字段 | String[] | 关系实体所有可查询字段 |
optional | 关系是否可选,影响关联查询时使用LEFT JOIN(true)还是JOIN | boolean | false |
# 多对多关系
多对多关系目前支持下列两种场景:
- 存在中间表实体,中间表包含两个外键,分别关联两个实体;
- 不存在中间实体,但是当前实体存在关系实体的ids列表字段,我们称为内嵌关系字段;
# 中间表实体
设从 User 用户实体到 Role 角色实体建立多对多关系,UserRole 为中间表,UserRole 包含两个多对一关系,分别关联 User 和 Role:
User 实体:
import fly.core.data.annotation.ManyToMany;
@Entity
@ManyToMany(name = "roles", targetEntity = Role.class, joinEntity = UserRole.class, defaultExpanding = { "code" })
public class User {
@UUID
protected String id;
// get/set
}
Role 实体:
@Entity
public class Role {
@UUID
protected String id;
@Column(length = 10)
protected String code;
// get/set
}
UserRole 实体,:
@Entity
public class UserRole {
@UUID
protected String id;
@ManyToOne(targetEntity = User.class)
protected String userId;
@ManyToOne(targetEntity = Role.class)
protected String roleId;
// get/set
}
# 内嵌关系字段
设从 User 用户实体到 Role 角色实体建立多对多关系,User 实体存在一 ids 字段,数据库存储的数据结构为:["id1","id2"]:
User 实体:
import fly.core.data.annotation.ManyToMany;
@Entity
@ManyToMany(name = "roles", targetEntity = Role.class, embeddedField = "ids", defaultExpanding = { "code" })
public class User {
@UUID
protected String id;
@Column(definition = "LONGVARCHAR")
protected Set<String> ids;
// get/set
}
Role 实体:
@Entity
public class Role {
@UUID
protected String id;
@Column(length = 10)
protected String code;
// get/set
}