Eloquent ORM for Java
- 注册配置 Configuration
- 数据映射 Mapping
- 数据模型 Model
- 查询结果集 Record
- 查询构造器 Query Builder
- 关联关系 Relationship
- 生成代码 Generate
- GraalVM
- 版本信息 Version
数据表经常要与其它表做关联,比如一篇博客文章可能有很多评论,或者一个订单会被关联到下单用户
Eloquent 让组织和处理这些关联关系 Relationship变得简单,并且支持多种不同类型的关联关系 Relationship,更重要的是会进行查询优化,这点在多层级关系的情况下尤其明显
通过在entity中声明对应的属性, 并在属性上使用相关注解@HasOneOrMany(),@BelongsTo(),@BelongsToMany()标记
所有注解在包 gaarason.database.eloquent.annotation.* 中
现在, 不再需要指定目标模型, 程序会根据字段类型entity的找到正确的目标模型
重要:所有对应的关系键的java类型必须严格一致
最常见的关系场景
@HasOneOrMany() 其中包含2个属性:
sonModelForeignKey表示子表的外键localModelLocalKey表示本表的关联键, 默认值为本表的主键(@Primary()修饰的键)
以下是一个teacher包含一个pet(宠物)的例子
teacher
id - integer
pet
id - integer
master_id - integer
public class Teacher implements Serializable {
// ...
// 省略了`localModelLocalKey`, 表示本表关系键为主键(`@Primary()`修饰的键)
@HasOneOrMany(sonModelForeignKey = "master_id")
private Pet pet;
}同样使用@HasOneOrMany()注解, 用法也是一致的, 要注意的是使用此注解的属性需要是List<F>/F[]/ArrayList<F>/LinkedHashSet<F>/LinkedList<F>
/Set<F>类型
以下是一个teacher包含多个student的例子
teacher
id - integer
student
id - integer
teacher_id - integer
public class Teacher implements Serializable {
// ...
// 省略了`localModelLocalKey`, 表示本表关系键为主键(`@Primary()`修饰的键)
@HasOneOrMany(sonModelForeignKey = "teacher_id")
private List<Student> students;
}@BelongsTo() 其中包含2个属性:
localModelForeignKey表示本表的外键parentModelLocalKey表示父表的关联键, 默认值为父表的主键(@Primary()修饰的键)
以下是一个teacher包含多个student的场景下, 需要从student找到teacher的例子
teacher
id - integer
student
id - integer
teacher_id - integer
public class Student implements Serializable {
// ...
// 省略了`parentModelLocalKey`, 表示父表的关联键为父表主键(`@Primary()`修饰的键)
@BelongsTo(localModelForeignKey = "teacher_id")
private Teacher teacher;
}@BelongsToMany() 其中包含5个属性:
-
relationModel表示关系表的模型 -
localModelLocalKey表示本表中关联键, 默认值为本表的主键(@Primary()修饰的键) -
foreignKeyForLocalModel表示关系表中关联本表的外键 -
foreignKeyForTargetModel表示关系表中关联目标表的外键 -
targetModelLocalKey表示目标表中关联键, 默认值为目标表的主键(@Primary()修饰的键) -
使用此注解的属性需要是
List<F>/F[]/ArrayList<F>/LinkedHashSet<F>/LinkedList<F>/Set<F>类型
以下是一个teacher包含多个student,同时, 一个student包含多个teacher的场景, 关系表使用relationship_student_teacher
teacher
id - integer
student
id - integer
relationship_student_teacher
teacher_id - integer
student_id - integer
public class Student implements Serializable {
// ...
// 省略了`localModelLocalKey`, 表示`本表`中`关联键`, 默认值为`本表`的主键(`@Primary()`修饰的键)
// 省略了`targetModelLocalKey`, 表示`目标表`中`关联键`, 默认值为`目标表`的主键(`@Primary()`修饰的键)
// 当上述均成立时, 关系成立
@BelongsToMany(relationModel = RelationshipStudentTeacherModel.class,
foreignKeyForLocalModel = "teacher_id", foreignKeyForTargetModel = "student_id")
private List<Student> students;
}以上是student维度的建立, teacher维度的类似, 暂略
灵活的关系场景
多态关联允许目标模型借助单个关联从属于多个模型
例如,你正在构建一个允许用户共享博客文章和视频的应用程序,其中 Comment 模型可能同时从属于 Post 和 Video 模型, 甚至包括他自己 Comment。
用法上, 相比较于常规关系, 复用了对应的注解, 但在注解中增加了额外的多态属性, 用于指明其多态的规则实现
@HasOneOrMany() 其中包含2个多态属性:
sonModelMorphKey表示子表中的多态类型键sonModelMorphValue表示子表中的多态类型键的值, 默认值为本表的表名
以下是Comment同时从属于 Post 和 他自己的场景
post
id - integer
comment
id - integer
p_type - string
p_id - integer
以下是一个Post包含一个Comment的场景的定义
public class Post extends BaseEntity {
// ...
// 省略了`sonModelMorphValue`, 表示当 p_type 的值为 Post的表名时, 关系成立
@HasOneOrMany(sonModelForeignKey = "p_id", sonModelMorphKey = "p_type")
private Comment comment;
}以下是一个Comment包含一个Comment的场景的定义
public class Comment extends BaseEntity {
// ...
// 省略了`sonModelMorphValue`, 表示当 p_type 的值为 Comment的表名时, 关系成立
@HasOneOrMany(sonModelForeignKey = "p_id", sonModelMorphKey = "p_type")
private Comment comment;
}同样使用@HasOneOrMany()注解, 用法也是一致的, 要注意的是使用此注解的属性需要是List<F>/F[]/ArrayList<F>/LinkedHashSet<F>/LinkedList<F>
/Set<F>类型
以下是Comment同时从属于 Post 和 他自己的场景
post
id - integer
comment
id - integer
p_type - string
p_id - integer
以下是一个Post包含多个Comment的场景的定义
public class Post extends BaseEntity {
// ...
// 省略了`sonModelMorphValue`, 表示当 p_type 的值为 Post的表名时, 关系成立
@HasOneOrMany(sonModelForeignKey = "p_id", sonModelMorphKey = "p_type")
private List<Comment> comments;
}以下是一个Comment包含多个Comment的场景的定义
public class Comment extends BaseEntity {
// ...
// 省略了`sonModelMorphValue`, 表示当 p_type 的值为 Comment的表名时, 关系成立
@HasOneOrMany(sonModelForeignKey = "p_id", sonModelMorphKey = "p_type")
private List<Comment> comments;
}@BelongsTo() 其中包含2个多态属性:
localModelMorphKey表示本表中的多态类型键localModelMorphValue表示本表中的多态类型键的值, 默认值为父表的表名
以下是Comment同时从属于 Post 和 他自己的场景
post
id - integer
comment
id - integer
p_type - string
p_id - integer
以下是一个Comment从属与Comment以及Post的场景的定义
public class Comment extends BaseEntity {
// ...
// 省略了`localModelMorphValue`, 表示当 p_type 的值为 Post的表名时, 关系成立
@BelongsTo(localModelForeignKey = "p_id", localModelMorphKey = "p_type")
private Post post;
// 省略了`localModelMorphValue`, 表示当 p_type 的值为 Comment的表名时, 关系成立
@BelongsTo(localModelForeignKey = "p_id", localModelMorphKey = "p_type")
private Comment pcomment;
}@BelongsToMany() 其中包含4个多态属性:
morphKeyForLocalModel表示关系表中的本表的多态类型键morphValueForLocalModel表示关系表中的本表的多态类型键的值, 默认值为本表的表名morphKeyForTargetModel表示关系表中的目标表的多态类型键morphValueForTargetModel``关系表中的目标表的多态类型键的值, 默认值为目标表的表名
以下是Comment和Post同时与Image存在多对多关系的场景, 其中Relation为中间表, 用于实现多态
post
id - integer
comment
id - integer
image
id - integer
relation
relation_one_type - string
relation_one_value - integer
relation_two_type - string
relation_two_value - integer
以下是一个Post与Image多对多关系的定义
public class Post extends BaseEntity {
// ...
// 省略了`morphValueForLocalModel`, 表示当 relation_one_value 的值为 Post的表名时, 和本表(post)关系成立
// 省略了`morphValueForTargetModel`, 表示当 relation_two_value 的值为 Image的表名时, 和目标表(image)关系成立
// 当上述均成立时, 关系成立
@BelongsToMany(relationModel = SuperRelation.Model.class, foreignKeyForLocalModel = "relation_one_value", foreignKeyForTargetModel = "relation_two_value",
morphKeyForLocalModel = "relation_one_type", morphKeyForTargetModel = "relation_two_type")
private List<Image> imagesWithMorph;
}以下是一个Comment与Image多对多关系的定义
public class Comment extends BaseEntity {
// ...
// 省略了`morphValueForLocalModel`, 表示当 relation_one_value 的值为 Comment的表名时, 和本表(comment)关系成立
// 省略了`morphValueForTargetModel`, 表示当 relation_two_value 的值为 Image的表名时, 和目标表(image)关系成立
// 当上述均成立时, 关系成立
@BelongsToMany(relationModel = SuperRelation.Model.class, foreignKeyForLocalModel = "relation_one_value", foreignKeyForTargetModel = "relation_two_value",
morphKeyForLocalModel = "relation_one_type", morphKeyForTargetModel = "relation_two_type")
private List<Image> imagesWithMorph;
}以下是一个Image同时与Comment以及Post多对多关系的定义
public class Image extends BaseEntity {
// ...
// 省略了`morphValueForLocalModel`, 表示当 relation_two_value 的值为 Image的表名时, 和本表(image)关系成立
// 省略了`morphValueForTargetModel`, 表示当 relation_one_value 的值为 Post的表名时, 和目标表(post)关系成立
// 当上述均成立时, 关系成立
@BelongsToMany(relationModel = SuperRelation.Model.class, foreignKeyForLocalModel = "relation_two_value", foreignKeyForTargetModel = "relation_one_value",
morphKeyForLocalModel = "relation_two_type", morphKeyForTargetModel = "relation_one_type")
private List<Post> posts;
// 省略了`morphValueForLocalModel`, 表示当 relation_two_value 的值为 Image的表名时, 和本表(image)关系成立
// 省略了`morphValueForTargetModel`, 表示当 relation_one_value 的值为 Comment的表名时, 和目标表(comment)关系成立
// 当上述均成立时, 关系成立
@BelongsToMany(relationModel = SuperRelation.Model.class, foreignKeyForLocalModel = "relation_two_value", foreignKeyForTargetModel = "relation_one_value",
morphKeyForLocalModel = "relation_two_type", morphKeyForTargetModel = "relation_one_type")
private List<Comment> comments;
}以下是中间表SuperRelation以及其model的定义 (普通的定义)
public class SuperRelation extends BaseEntity {
// ...
@Column(name = "relation_one_type", length = 200L)
private String relationOneType;
@Column(name = "relation_one_value", unsigned = true)
private Long relationOneValue;
@Column(name = "relation_two_type", length = 200L)
private String relationTwoType;
@Column(name = "relation_two_value", unsigned = true)
private Long relationTwoValue;
public static class Model extends BaseEntity.BaseModel<SuperRelation, Long> {
}
}用于自定义不同的数据模型之间的关系
自定义的注解可以和预置的注解一样使用
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
// 使用 @Relation 标注这是一个关联关系的注解, 并指明其解析器
@Relation(HasOneQueryRelation.class)
public @interface HasOneCustom {
/**
* `子表`中的`关联本表的外键`
* @return `子表`中的`关联本表的外键`
*/
String sonModelForeignKey();
/**
* `本表`中的`关联键`, 默认值为`本表`的主键(`@Primary()`修饰的键)
* @return `本表`中的`关联键`
*/
String localModelLocalKey() default "";
}- 实现
RelationSubQuery的各个方法, 建议参考预置的解析器, 这部分的逻辑比较复杂与繁琐 - 提供此构造函数
public constructor(Field field, ModelShadowProvider modelShadowProvider, Model<?, ?> model)
public static class HasOneQueryRelation extends BaseRelationSubQuery implements RelationSubQuery {
// 提供此构造函数
public HasOneQueryRelation(Field field, ModelShadowProvider modelShadowProvider, Model<?, ?, ?> model) {
super(modelShadowProvider, model);
}
// ... 实现 RelationSubQuery 的各个方法
}- 也可以直接继承
预置的解析器 - 当他们功能相近时,会更加方便
public static class HasOneQueryRelation extends HasOneOrManyQueryRelation {
public HasOneQueryRelation(Field field, ModelShadowProvider modelShadowProvider, Model<?, ?, ?> model) {
super(field, modelShadowProvider, model);
}
@Override
protected HasOneOrManyTemplate initTemplate(Field field) {
HasOne hasOne = field.getAnnotation(HasOne.class);
Model<?, ?> sonModel = getModelInstance(field);
String sonModelForeignKey = hasOne.sonModelForeignKey();
String localModelLocalKey = "".equals(hasOne.localModelLocalKey()) ? getPrimaryKeyColumnName(sonModel) :
hasOne.localModelLocalKey();
return new HasOneOrManyTemplate(sonModel, sonModelForeignKey, localModelLocalKey, "", "");
}
// ... 可选 覆盖父类的各个方法, 以实现自定义逻辑
}- 在实体中, 和预置的注解一样的使用方式
public class Entity implements Serializable {
// .. 其他数据库字段
// 和预置的注解一样的使用方式
@HasOneCustom(sonModelForeignKey = "student_id")
private RelationshipStudentTeacher relationshipStudentTeacher;
}当使用firstOrFail()等方法获取查询结果时, 可以通过with()方法声明需要关联的属性(需要事先在实体对象entity中定义)
由于 Eloquent 所有关联关系都是通过实体对象entity的属性定义的,所以只有当用toObject()或者toObjectList()转化为实体对象entity时, 他们的定义才会生效
Record::with(String column, GenerateSqlPart builderClosure, RelationshipRecordWith recordClosure)
与 RecordList::with() 与 Builer::with() 方法签名类似, 接受3个参数
column希望执行关联的属性名(非数据库字段), 可以使用.快捷指定下级builderClosure所关联的Model的查询构造器约束recordClosure所关联的Model的再一级关联, 可以指定下级
下面是一些例子, 基本都可以在database-core-test模块的单元测试中找到
// select * from student limit 1
// select * from teacher where id in (?)
Student student = studentModel.newQuery().with("teacher").firstOrFail().toObject();// 多级简单一对一
// select * from student limit 1
// select * from teacher where id in (?)
// select * from pet where id in (?)
Student student = studentModel.newQuery().firstOrFail().with("teacher.pet").toObject();// 多级简单一对一包含筛选
// select * from student limit 1
// select * from teacher where id in (?) and age > 32
Student student = studentModel.newQuery().with("teacher", bulider -> bulider.where("age",">","32")).firstOrFail().toObject();// select * from `student` limit 1
// select * from `teacher` where `id`in("6")
// select * from `student` where `teacher_id`in("6")
// select * from `student` where `id`in("2","3") and `teacher_id`in("6")
Student student = studentModel.newQuery().firstOrFail().with("teacher", builder -> builder
.with("students", builder1 -> builder1
.with("teacher", builder2 -> builder2
.with("students", builder3 -> builder3.whereIn("id", "3", "2")
.with("teacher"))))).toObject();// select * from `student` limit 1
// select * from `teacher` where `id`in("6")
// select * from `student` where `teacher_id`in("6")
// select * from `relationship_student_teacher` where `student_id`in("1","2","3","4")
// select * from `teacher` where `id`in("1","2","6")
// select * from `student` where `teacher_id`in("1","2","6")
// select * from `teacher` where `id`in("1","2","6")
// select * from `student` where `id`in("2","3") and `teacher_id`in("1","2","6")
Student student = studentModel.newQuery().firstOrFail().with("teacher.students.relationshipStudentTeachers", builder -> builder
.with("teacher.students.teacher.students.teacher.students", builder1 -> builder1.whereIn("id", "3", "2").with("teacher"))).toObject();// select count(*) as '7f9a1374-22fe-461e-8b80-66509c2ff7a6' from `student` limit 1
// select * from `student` order by `id` asc limit 0,4
// select * from `relationship_student_teacher` where `student_id`in("1","2","3","4")
// select * from `teacher` where `id`in("1","2","6")
// select * from `relationship_student_teacher` where `teacher_id`in("1","2","6") order by `student_id` asc
// select * from `student` where `id`in("1","2","3","4","5","6","7","8","9","10")
Paginate<Student> paginate = studentModel.newQuery().orderBy("id").with("relationshipStudentTeachers.teacher.relationshipStudentTeachers",
builder -> builder.orderBy("student_id").with("student")).paginate(1, 4);- 在定义
entity时, 除了通过@BelongsToMany注解定义与目标表的多堆多关系时, 还可以通过@HasOneOrMany定义与中间表的一对多关系 - 在查询中间表时, 直接
with对应的一对多关系即可
studentModel..newQuery().with("teachers").with("relation").get();
// 等价于
studentModel..newQuery().with("teachers", "relation").get();
// 注意 with("teachers", "relation") 与 with("teachers.relation") 是完全不同的含义.- 有时你可能需要计算给定关系的相关模型的数量, 或者仅仅想知道其中最大的某项是什么, 而不实际加载模型
- 针对于一对一/一对多/反向一对一/多对多, 以及其对应的多态形式的所有关联关系, 均进行了支持
- 计算指定关联关系的数量, 并将结果拖地在实体上
- 默认的属性是
{relationFieldName}Count
// 在实体中定义用于接受结果的属性
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Table(name = "teacher")
public class Teacher extends BaseEntity implements Serializable {
// ... 省略其他字段
@HasOneOrMany(sonModelForeignKey = "teacher_id")
private List<Student> students;
// ---------------- -----------------//
@Column(inDatabase = false)
private Long studentsCount;
}- withCount(relationFieldName)
// 统计id为 1,2,6的三位老师, 分别有几个学生
List<Teacher> teachers = teacherModel.newQuery()
.whereIn(Teacher::getId, 1, 2, 6)
.orderBy(Teacher::getId)
.withCount(Teacher::getStudents)
.get()
.toObjectList();
// 查看结果
teachers.get(0).getStudentsCount()- withCount(relationFieldName, 关系表中的统计列, 结果落地的属性)
// 统计id为 1,2,6的三位老师, 的学生们拥有id的数量, 并落地在 studentCount 属性上
List<Teacher> teachers = teacherModel.newQuery()
.whereIn(Teacher::getId, 1, 2, 6)
.orderBy(Teacher::getId)
.withCount(Teacher::getStudents, Student::getId, Teacher::getStudentsCount)
.get()
.toObjectList();
// 查看结果
teachers.get(0).getStudentsCount()- withCount(relationFieldName, 关系表中的统计列, 自定义查询 ,结果落地的属性)
// 统计id为 1,2,6的三位老师的, sex=2的学生们拥有id的数量, 并落地在 studentsCount 属性上
List<Teacher> teachers = teacherModel.newQuery()
.whereIn(Teacher::getId, 1, 2, 6)
.orderBy(Teacher::getId)
.withCount(Teacher::getStudents, Student::getId, builder -> builder.where(Student::getSex, 2), Teacher::getStudentsCount)
.get()
.toObjectList();
// 查看结果
teachers.get(0).getStudentsCount()withMax/withMin/withAvg/withSum- 计算指定关联关系的指定列的
统计, 并将结果拖地在实体上 - 默认的属性是
{relationFieldName}{function}{column}
- 计算指定关联关系的指定列的最大值, 并将结果拖地在实体上
- 默认的属性是
{relationFieldName}Max{column}
// 在实体中定义用于接受结果的属性
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Table(name = "teacher")
public class Teacher extends BaseEntity implements Serializable {
// ... 省略其他字段
@BelongsToMany(relationModel = RelationshipStudentTeacherModel.class,
foreignKeyForLocalModel = "teacher_id", foreignKeyForTargetModel = "student_id",
localModelLocalKey = "id", targetModelLocalKey = "id")
private Student[] students;
// ---------------- -----------------//
@Column(inDatabase = false)
private Long studentsMaxAge;
}- withMax(relationFieldName, 关系表中的统计列)
// 分别统计id为 1,2,6的三位老师的, 学生们中最大的age, 并落地在 studentsMaxAge 属性上
List<Teacher> teachers = teacherModel.newQuery()
.whereIn(Teacher::getId, 1, 2, 6)
.orderBy(Teacher::getId)
.withMax(Teacher::getStudents, Student::getAge)
.get()
.toObjectList();
// 查看结果
teachers.get(0).getStudentsMaxAge()- withMax(relationFieldName, 关系表中的统计列, 结果落地的属性)
// 分别统计id为 1,2,6的三位老师的, 学生们中最大的id, 并落地在 studentsMaxAge 属性上
List<Teacher> teachers = teacherModel.newQuery()
.whereIn(Teacher::getId, 1, 2, 6)
.orderBy(Teacher::getId)
.withMax(Teacher::getStudents, Student::getId, Teacher::getStudentsMaxAge)
.get()
.toObjectList();
// 查看结果
teachers.get(0).getStudentsMaxAge()- withMax(relationFieldName, 关系表中的统计列, 自定义查询, 结果落地的属性)
// 分别统计id为 1,2,6的三位老师的, sex=2 的学生们中最大的id, 并落地在 studentsMaxAge 属性上
List<Teacher> teachers = teacherModel.newQuery()
.whereIn(Teacher::getId, 1, 2, 6)
.orderBy(Teacher::getId)
.withMax(Teacher::getStudents, Student::getId, builder -> builder.where(Student::getSex, 2), Teacher::getStudentsMaxAge)
.get()
.toObjectList();
// 查看结果
teachers.get(0).getStudentsMaxAge()略
略
略
- 使用从表关系筛选主表结果
- 检索模型记录时, 你可能希望根据关系的存在限制结果. 例如, 假设要检索至少有一条评论的所有博客文章.
使用 where exists 实现的 whereHas语句
当主要查询表的数据量(条件约束后), 小于, 从表的数据量(条件约束后)时, 推荐使用.
- whereHas/whereNotHas(relationFieldName)
// 查询所有有学生的老师
// select * from teacher where exists (select * from student where `teacher`.`id`=`student`.`teacher_id`)
List<Teacher> teacherList = teacherModel.newQuery()
.whereHas(Teacher::getStudentArray)
.get()
.toObjectList();- whereHas/whereNotHas(relationFieldName, 自定义查询)
// 查询所有的老师, 这些老师的学生们都不大于16岁
// select * from teacher where not exists (select * from student where `age`>"16" and `teacher`.`id`=`student`.`teacher_id`) order by `id` asc
List<Teacher> teacherList = teacherModel.newQuery()
.whereNotHas(Teacher::getStudentArray, builder -> builder.where(Student::getAge, ">", "16"))
.orderBy(Teacher::getId)
.get()
.toObjectList();
// 查询所有学生, 这些学生的有男老师
// select * from student where exists (select * from teacher where `sex`="1" and `teacher`.`id`=`student`.`teacher_id`) order by `id` asc
List<Student> students = studentModel.newQuery()
.whereHas("teacher", builder -> builder.where("sex", 1))
.orderBy(Student::getId)
.get()
.toObjectList();使用 where in 实现的 whereHas语句
当主要查询表的数据量(条件约束后), 大于, 从表的数据量(条件约束后)时, 推荐使用.
- whereHasIn/whereNotHasIn(relationFieldName)
// 查询所有有学生的老师
// select * from teacher where id in (select teacher_id from student)
List<Teacher> teacherList = teacherModel.newQuery()
.whereHasIn(Teacher::getStudentArray)
.get()
.toObjectList();- whereHasIn/whereNotHasIn(relationFieldName, 自定义查询)
// 查询所有的老师, 这些老师的学生们都不大于16岁
// select * from teacher where id in (select teacher_id from student where`age`>"16") order by `id` asc
List<Teacher> teacherList = teacherModel.newQuery()
.whereNotHasIn(Teacher::getStudentArray, builder -> builder.where(Student::getAge, ">", "16"))
.orderBy(Teacher::getId)
.get()
.toObjectList();
// 查询所有学生, 这些学生的有男老师
// select * from student where teacher_id in (select id from teacher where`sex`="1") order by `id` asc
List<Student> students = studentModel.newQuery()
.whereHasIn("teacher", builder -> builder.where("sex", 1))
.orderBy(Student::getId)
.get()
.toObjectList();- 处理多对多关联的时候,Eloquent 还提供了一些额外的辅助函数使得处理关联模型变得更加方便。
- 这些关系的都需要在 Entity 中进行声明, 并在 Record 中使用。
- 在 Record 中使用时, 需要先用
bind()指明要处理的关系(属性名) - 需要注意的是一下的 4 类操作, 均可在全部 3 类关系上使用, 但是中间表数据插入仅对
@BelongsToMany关系生效 - 4 类操作在使用集合作为参数时, 参数代表的含义是主键集合(并不是关系键, 程序会根据注解中的声明找到真正的关系键)
我们假定一个用户可能有多个角色,同时一个角色属于多个用户,要通过在连接模型的中间表中插入记录附加角色到用户上,可以使用 attach 方法
- @HasOneOrMany : 会将子表(目标表)的外键的值更新为本表的关系键值
- @BelongsTo : 会将本表的外键的值更新为父表(目标表)的关系键值
- @BelongsToMany : 在中间表中新增记录, 2个外键分表指向本表的关系键与目标表的关系键, 可以指定附加的字段
Record<User, int> userRecord = UserModel.findOrFail(1);
// 以下2种写法等价, 返回受影响的行数
userRecord.bind("roles").attach(RoleModel.findMany(1,2));
userRecord.bind("roles").attach(Arrays.asList(1, 2));
// 附加关联关系到模型,还可以以MAP形式传递额外被插入数据到中间表
// 以下2种写法等价
HashMap<String, Object> map = new HashMap<>();
map.put("note", note);
userRecord.bind("roles").attach(RoleModel.findMany(1,2), map);
userRecord.bind("roles").attach(Arrays.asList(1, 2), map);当然,有时候有必要从用户中移除角色,要移除一个关联记录,使用 detach 方法。
- @HasOneOrMany : 会将子表(目标表)的外键的值更新为默认值(String则为"", integer则为"0")
- @BelongsTo : 会将本表的外键的值更新为默认值(String则为"", integer则为"0")
- @BelongsToMany : 在中间表中移除相应的记录, 但是,两个模型在数据库中都保持不变
Record<User, int> userRecord = UserModel.findOrFail(1);
// 以下2种写法等价, 返回受影响的行数
userRecord.bind("roles").detach(RoleModel.findMany(1,2));
userRecord.bind("roles").detach(1,2);有时候有要将用户更新到指定的角色, 任何不在指定范围对应记录将会移除, 使用 sync 方法。
- @HasOneOrMany : 针对每个范围内的值, 将会调用
attach与detach - @BelongsTo : 针对每个范围内的值, 将会调用
attach与detach - @BelongsToMany : 针对每个范围内的值, 将会调用
attach与detach,两个模型在数据库中都保持不变, 可以指定附加的字段在增加关系时生效
Record<User, int> userRecord = UserModel.findOrFail(1);
// 以下2种写法等价, 返回受影响的行数
userRecord.bind("roles").sync(RoleModel.findMany(1,2));
userRecord.bind("roles").sync(1,2);多对多关联还提供了一个 toggle 方法用于切换给定 ID 的附加状态,如果给定ID当前被附加,则取消附加,类似的,如果当前没有附加,则附加, 使用 toggle 方法。
- @HasOneOrMany : 针对每个范围内的值, 将会调用
attach与detach - @BelongsTo : 针对每个范围内的值, 将会调用
attach与detach - @BelongsToMany : 针对每个范围内的值, 将会调用
attach与detach,两个模型在数据库中都保持不变, 可以指定附加的字段在增加关系时生效
Record<User, int> userRecord = UserModel.findOrFail(1);
// 以下2种写法等价, 返回受影响的行数
userRecord.bind("roles").toggle(RoleModel.findMany(1,2));
userRecord.bind("roles").toggle(1,2);