原文:https://www.zhihu.com/question/64187262/answer/217667224
首先,我们整一个Classes和Student,这个很明显了吧,一个班里面有多个学生,然后我们不使用连接表来表示关系,即student表中有一个外键表明这个学生是哪个班的。
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
@ManyToOne(targetEntity = Classes.class, cascade = CascadeType.ALL)
@JoinColumn(name = "classesId")
private Classes classes;
// 省略了setter、getter这些乱七八糟的东西
}
@Entity
public class Classes {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
@OneToMany(targetEntity=Student.class,cascade=CascadeType.ALL)
@JoinColumn(name="classesId")
private Set<Student> students = new HashSet<>();
// 省略了setter、getter这些乱七八糟的东西
}
运行下面代码:
Session session = ...;
Transaction tx = session.beginTransaction();
Student s1 = new Student("aaa");
Student s2 = new Student("bbb");
Student s3 = new Student("ccc");
Classes c1 = new Classes("111");
Classes c2 = new Classes("222");
c1.getStudents().add(s1);
c1.getStudents().add(s2);
s3.setClasses(c2);
session.save(c1);
session.save(s3);
tx.commit();
session.close();
生成的表以及里面的数据和我们想的一样:
mysql> desc classes;
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
+-------+--------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
mysql> desc student;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| classesId | int(11) | YES | MUL | NULL | |
+-----------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql> select * from classes;
+----+------+
| id | name |
+----+------+
| 1 | 111 |
| 2 | 222 |
+----+------+
2 rows in set (0.00 sec)
mysql> select * from student;
+----+------+-----------+
| id | name | classesId |
+----+------+-----------+
| 1 | aaa | 1 |
| 2 | bbb | 1 |
| 3 | ccc | 2 |
+----+------+-----------+
3 rows in set (0.00 sec)
修改Classes类为如下,Student类不变:
@Entity
public class Classes {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
@OneToMany(targetEntity=Student.class,cascade=CascadeType.ALL,mappedBy="classes")
// 注意没有@JoinColumn
private Set<Student> students = new HashSet<>();
}
仍然将上面的代码跑一遍。生成的表结构和之前是一样的就不看的,直接看数据:
mysql> select * from classes;
+----+------+
| id | name |
+----+------+
| 1 | 111 |
| 2 | 222 |
+----+------+
2 rows in set (0.00 sec)
mysql> select * from student;
+----+------+-----------+
| id | name | classesId |
+----+------+-----------+
| 1 | aaa | NULL |
| 2 | bbb | NULL |
| 3 | ccc | 2 |
+----+------+-----------+
3 rows in set (0.00 sec)
差异还是很明显的,mappedBy="classes"
表示一方放弃维护权,并将维护权交给了对方的关联属性classes,这个维护到底是要干些啥?在这里不就是去设置student表中的classesId列的值嘛。看这几行代码:
c1.getStudents().add(s1);
c1.getStudents().add(s2);
s3.setClasses(c2);
session.save(c1);
session.save(s3);
没有mappedBy的时候,大家都要维护,也就是说第四行保存c1的时候,除了要保存c1,s1,s2外,对应的student表中相应记录的classesId的值要被正确的设置,至于是在保存s1和s2的时候设置还是另外使用update语句去设置我这里就不谈了。同样的道理,第五行保存s3的时候,除了保存s3和c2外,对应的student表中相应记录的classesId的值要被正确的设置。
有mappedBy的时候,表示Classes的students 字段放弃维护权,而将维护权交给了Student的关联属性classes。在第四行保存c1的时候,就只保存了c1,s1,s2,由于他放弃了维护权,所以student表中相应记录的classesId的值并没有被正确的设置(也就是上面Student表中的两个NULL)。但是反过来,Student类的classes字段是有维护权的,所以第五行保存s3的时候,除了保存s3和c2外,对应的student表中相应记录的classesId的值要被正确的设置
@OneToOne @OneToMany @ManyToMany 中都有mappedBy属性:
1.一旦指定该属性,则表明当前实体不能控制关联关系
2.当前实体放弃控制关联关系之后,不允许使用 @JoinColumn @JoinTable 修饰代表关联实体的属性
(转载本站文章请注明作者和出处 hibernate里面@mappedBy的作用? )