目录结构,看图:
Student.java
1 package cn.itcast.bean;
2
3 import java.util.HashSet;
4 import java.util.Set;
5
6 import javax.persistence.CascadeType;
7 import javax.persistence.Column;
8 import javax.persistence.Entity;
9 import javax.persistence.GeneratedValue;
10 import javax.persistence.Id;
11 import javax.persistence.JoinColumn;
12 import javax.persistence.JoinTable;
13 import javax.persistence.ManyToMany;
14
15 @Entity
16 public class Student {
17 private Integer id;
18 private String name;
19 private Set<Teacher> teachers = new HashSet<Teacher>();
20
21 public Student() {
22 }
23
24 public Student(String name) {
25 this.name = name;
26 }
27
28 @Id
29 @GeneratedValue
30 // id作为实体标识符 并且采用数据库的id自增长方式生成主键值
31 public Integer getId() {
32 return id;
33 }
34
35 public void setId(Integer id) {
36 this.id = id;
37 }
38
39 @Column(length = 10, nullable = false)
40 public String getName() {
41 return name;
42 }
43
44 public void setName(String name) {
45 this.name = name;
46 }
47
48 @ManyToMany(cascade = CascadeType.REFRESH)
49 @JoinTable(name = "student_teacher", inverseJoinColumns = @JoinColumn(name = "teacher_id"), joinColumns = @JoinColumn(name = "student_id"))
50 public Set<Teacher> getTeachers() {
51 return teachers;
52 }
53
54 /*
55 假如不对关联表里的字段做任何设定,那么表里面的字段默认由JPA的实现产品来帮我们自动生成。
56 inverseJoinColumns:inverse中文是反转的意思,但是觉得太恶心了,在JPA里,可以理解为被维护端
57 inverseJoinColumns被维护端外键的定义 @JoinColumn:外键,中间表哪个外键名称跟teacher表的主键关联
58 joinColumns:关系维护端的定义
59 */
60
61 public void setTeachers(Set<Teacher> teachers) {
62 this.teachers = teachers;
63 }
64
65 public void addTeacher(Teacher teacher) {
66 this.teachers.add(teacher);
67 }
68
69 public void removeTeacher(Teacher teacher) {
70 if(this.teachers.contains(teacher)){ //凭什么判断teacher在集合teachers中呢?是根据teacher的id
71 //这就有必要重写Teacher.java的hasCode和equals方法,通过这两个方法来判断对象是否相等
72 this.teachers.remove(teacher);
73 }
74 }
75 }
Teacher.java
1 package cn.itcast.bean;
2
3 import java.util.HashSet;
4 import java.util.Set;
5
6 import javax.persistence.CascadeType;
7 import javax.persistence.Column;
8 import javax.persistence.Entity;
9 import javax.persistence.GeneratedValue;
10 import javax.persistence.Id;
11 import javax.persistence.ManyToMany;
12
13 @Entity
14 public class Teacher {
15 private Integer id;
16 private String name;
17 private Set<Student> students = new HashSet<Student>();
18
19 public Teacher() {
20 }
21
22 public Teacher(String name) {
23 this.name = name;
24 }
25
26 @Id
27 @GeneratedValue
28 // id作为实体标识符 并且采用数据库的id自增长方式生成主键值
29 public Integer getId() {
30 return id;
31 }
32
33 public void setId(Integer id) {
34 this.id = id;
35 }
36
37 @Column(length = 10, nullable = false)
38 public String getName() {
39 return name;
40 }
41
42 public void setName(String name) {
43 this.name = name;
44 }
45
46 @ManyToMany(cascade = CascadeType.REFRESH, mappedBy = "teachers")
47 public Set<Student> getStudents() {
48 return students;
49 }
50
51 /*
52 cascade: CascadeType.PERSIST:级联保存不要,学生没来之前,老师就已经在了
53 CascadeType.MERGE:级联更新不要,把学生的信息改了,没必要修改相应的老师的信息,压根就没这业务需求
54 CascadeType.REMOVE:级联删除更不要,如果双方都设了级联删除,加入删除学生,会删除相应的老师,被删除的老师又
55 跟学生发生千丝万缕的关系,又把一批学生删掉.....没完没了...最终的结果可能是数据里面所有的记录都被清掉
56 所以在多对多关系中,级联删除通常是用不上的 这里只需设置级联涮新CascadeType.PERSIST就可以了,事实上refresh方法也很少使用
57 mappedBy: 通过这个属性来说明老师是关系被维护端 fetch: 加载行为默认是延迟加载(懒加载),凭Many。 这里不需要设置
58 */
59
60 public void setStudents(Set<Student> students) {
61 this.students = students;
62 }
63
64 @Override
65 public int hashCode() {
66 final int prime = 31;
67 int result = 1;
68 result = prime * result + ((id == null) ? 0 : id.hashCode());
69 //判断的依据是,如果id不为null的话,就返回id的哈希码
70 return result;
71 }
72
73 @Override
74 public boolean equals(Object obj) {
75 if (this == obj)
76 return true;
77 if (obj == null)
78 return false;
79 if (getClass() != obj.getClass())
80 return false;
81 final Teacher other = (Teacher) obj;
82 if (id == null) {
83 if (other.id != null)
84 return false;
85 } else if (!id.equals(other.id))
86 return false;
87 return true;
88 }
89 }
ManyToManyTest.java
1 package junit.test;
2
3 import javax.persistence.EntityManager;
4 import javax.persistence.EntityManagerFactory;
5 import javax.persistence.Persistence;
6
7 import org.junit.BeforeClass;
8 import org.junit.Test;
9
10 import cn.itcast.bean.Student;
11 import cn.itcast.bean.Teacher;
12
13 public class ManyToManyTest {
14
15 @BeforeClass
16 public static void setUpBeforeClass() throws Exception {
17 }
18
19 @Test
20 public void save() {
21 EntityManagerFactory factory = Persistence.createEntityManagerFactory("itcast");
22 EntityManager em = factory.createEntityManager();
23 em.getTransaction().begin();
24
25 em.persist(new Student("小张同学"));
26 em.persist(new Teacher("李勇老师"));
27
28 em.getTransaction().commit();
29 em.close();
30 factory.close();
31 }
32
33 /*
34 * 建立学生跟老师的关系
35 */
36 @Test
37 public void buildTS() {
38 EntityManagerFactory factory = Persistence.createEntityManagerFactory("itcast");
39 EntityManager em = factory.createEntityManager();
40 em.getTransaction().begin();
41
42 Student student = em.find(Student.class, 1);// 首先要得到学生,因为学生是关系维护端,通过关系维护端来建立关系
43 student.addTeacher(em.getReference(Teacher.class, 1));//这方法在业务意义上,就代表建立跟老师的关系
44 //所谓建立跟老师的关系,无非就是把老师加进集合里面去。
45 //建立关系,体现在JDBC上面,就是添加一条记录进中间表
46
47 em.getTransaction().commit();
48 em.close();
49 factory.close();
50 }
51
52 /*
53 * 解除学生跟老师的关系
54 */
55 @Test
56 public void deleteTS() {
57 EntityManagerFactory factory = Persistence.createEntityManagerFactory("itcast");
58 EntityManager em = factory.createEntityManager();
59 em.getTransaction().begin();
60
61 Student student = em.find(Student.class, 1);// 首先要得到学生,因为学生是关系维护端,通过关系维护端来建立关系
62 student.removeTeacher(em.getReference(Teacher.class, 1));//这方法在业务意义上,就代表解除跟老师的关系
63 //所谓解除跟老师的关系,无非就是把老师从集合里面删去
64 //解除关系,体现在JDBC上面,就是在中间表删除一条记录
65
66 em.getTransaction().commit();
67 em.close();
68 factory.close();
69 }
70
71 /*
72 * 删除老师,老师已经跟学生建立起了关系(错误写法)
73 */
74 @Test
75 public void deleteTeacher1() {
76 EntityManagerFactory factory = Persistence.createEntityManagerFactory("itcast");
77 EntityManager em = factory.createEntityManager();
78 em.getTransaction().begin();
79
80
81 em.remove(em.getReference(Teacher.class, 1));
82 //并不需要发生数据装载行为,只需要一个托管状态的实体,所以用getReference可以提供性能
83
84 em.getTransaction().commit();
85 em.close();
86 factory.close();
87 }
88 /*
89 该方法会出错,因为中间表中已经有记录了,会抛出以下错误
90 Caused by: java.sql.BatchUpdateException:
91 Cannot delete or update a parent row: a foreign key constraint fails
92 (`itcast/student_teacher`, CONSTRAINT `FKD4E389DE1D49449D` FOREIGN KEY (`teacher_id`)
93 REFERENCES `teacher` (`id`))
94 关系被维护端没有权力更新外键,所以不会删除中间表的记录
95 */
96
97 /*
98 * 删除老师,老师已经跟学生建立起了关系(正确写法)
99 */
100 @Test
101 public void deleteTeacher2() {
102 EntityManagerFactory factory = Persistence.createEntityManagerFactory("itcast");
103 EntityManager em = factory.createEntityManager();
104 em.getTransaction().begin();
105
106 Student student = em.find(Student.class, 1);
107 Teacher teacher = em.getReference(Teacher.class, 1);
108 //并不需要发生数据装载行为,只需要一个托管状态的实体,所以用getReference可以提供性能
109 student.removeTeacher(teacher);
110 em.remove(teacher);
111
112 em.getTransaction().commit();
113 em.close();
114 factory.close();
115 }
116
117 /*
118 * 删除学生,老师已经跟学生建立起了关系
119 */
120 @Test
121 public void deleteStudent() {
122 EntityManagerFactory factory = Persistence.createEntityManagerFactory("itcast");
123 EntityManager em = factory.createEntityManager();
124 em.getTransaction().begin();
125
126 Student student = em.getReference(Student.class, 1);
127 em.remove(student);//这样是可以删除学生的,尽管目前是有关系,中间表有关联记录,有外键约束
128 //但是我们现在要删除的是关系维护端,关系维护端是有权力对外键进行增删改查操作的
129 //删除的时候,Hibernate会判断student是关系维护端,然后去中间表把关联记录先删除掉
130 //再删掉student对象
131
132 em.getTransaction().commit();
133 em.close();
134 factory.close();
135 }
136 }