JPA概念解析-关联关系的维护

JPA中的关联关系的维护

数据库中的表之间一般有三种关系:一对一、一对多、多对多。

上面的三种关系都有主从表之分:例如

  • 一对一

    客户表(cst_customer)和客户详情表(cst_info):是先有的客户表,再有的客户详情表,因此客户表是主表,详情表是从表

  • 一对多

    房东表(landlord)和租户表(tenant):是先有的房东表然后才有的租户表,房东表是主表,租户表是从表

  • 多对多

    老师表(teacher)和学生表(student):是先有的老师表,才有的学生,所以老师表是主表,学生表是从表

上面的分析只是从生活实际出发,因为从生活实际出发,这样就更好理解主表和从表之间的关系了。

从上面三种表关系的举例中,不知道大家有没有发现一个共同特点:主表相当于一个leader,二从表就是一个follower,似乎follower更关心它和leader之间的关系。

而在使用JPA时,把维护表之间关系的权利交给谁呢?

肯定是交给follower啦!!

举例:(teacher和student,多对多关系)

老师表实体类:

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
@Entity // 指定这是一个实体类.在创建EntityManagerFactory的时候就会读取映射配置
@Table(name = "hb_teacher") // 指定该表所在数据库中的表名
public class Teacher {

@Id // 主键
@GeneratedValue(strategy = GenerationType.IDENTITY) // 主键生成策略
@Column(name = "t_id") // 主键在数据库中对应的字段名
private Long tid;


@Column(name = "t_name")
private String tname;

@Column(name = "t_age")
private String tage;

// targetEntity:映射的另一方实体的类
// mappedBy : 生命关系的维护方(也就是说放弃了维护关联关系的权利)
@ManyToMany(targetEntity = Student.class,mappedBy = "teachers")
private Set<Student> students = new HashSet<>(0);

public Teacher() {
}

// 省略get/set方法,省略toString方法
}

老师放弃了维护表之间关系的权利,通过设定mappedBy = "teachers"

学生表实体类:

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
@Entity
@Table(name = "hb_student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "s_id")
private Long sid;

@Column(name = "s_name")
private String sname;

@Column(name = "s_age")
private String sage;

@ManyToMany(targetEntity = Teacher.class,cascade = CascadeType.ALL)
@JoinTable(
name = "stu_tea_ref", // 中间表的表名
joinColumns = {
// 指定自己一方在表中维护的字段.name:字段名;referencedColumnName:关联的字段名称
@JoinColumn(name = "stu_id", referencedColumnName = "s_id")
},
inverseJoinColumns = {
// 指定对方在表中维护的字段.name:字段名;referencedColumnName:关联的字段的名称
@JoinColumn(name = "tea_id", referencedColumnName = "t_id")
}
)
private Set<Teacher> teachers = new HashSet<>(0);

public Student() {
}
// 省略get/set方法,省略toString方法
}

学生表维护了和老师表之间的关联关系,你看看它设置了那么多的字段,自己和老师在中间表中的字段都是自己负责设置的,通过设置字段joinColumnsinverseJoinColumns来指定。

实际上本应该就是这样,老师比少,学生比较多,让一个老师记住他教的所有的学生是很困难的,而把这个任务交个学生就相对简单多啦。

接下来,看看这样几种场景:

  1. 删除teacher表中的一条记录,如果该记录被中间表引用,能不能删除呢?

    答案是:如果没有设置级联删除,这个记录是不能删除的。试想他能删除,它删除以后,中间表中的数据怎么办,因为它是没有权利维护表关系的。

  2. 删除student表中的一条记录,如果该记录被中间表引用,能不能删除呢?

    答案是:是可以删除的。他删除以后,会连同中间表中和自己相关的数据也删除掉。因为它负责维护量表之间的关系!!

  3. 针对第一种情况,能不能删除呢,答案是可以的,但是,不但要在Teacher中设置@ManyToMany(targetEntity = Student.class, mappedBy = "teachers")标签中设置cascade = CascadeType.REMOVE),因为仅仅只这样设置的话,级联关系是整个表的字段,仅仅这样设置是很危险的。还应该应该给中间表与teacher表相关联的联合主键tea_id加上级联删除

    ALTER TABLE stu_tea_ref ADD CONSTRAINT tea_id FOREIGN KEY (tea_id) REFERENCES hb_teacher(t_id) ON DELETE CASCADE ;

    说明:stu_tea_ref是中间表,tea_id是引用teacher表主键的联合主键

    当删除teacher时,因为应用了级联删除,在中间表将与其主键值相同的相关记录也删除了,没有级联到其他的字段,student表不受影响.

不严谨之处,敬请指出,请关注我的博客:

Lee个人博客