新建了一个manyToMany的学生-课程表,学生可以选择多个课程,多个课程可以被学生选。尝试如下代码,创建了两个list,然后新建对象,加入list,然后set《list》,报错,这是因为new的新对象
都是暂时的,米有加入。但在这样会生成一个中间表,虽然此时保存了course,中间表需要student,而student是暂时对象,所以报错。
数据库如图:
student中啥也没有。因为没有保存,是暂时对象
错误代码:
student:
@ManyToMany
private List<Course> courses;
--------------------------------------
course:
@ManyToMany
private List<Student> students;
----------------------------------
测试代码:
List<Course> courses=new ArrayList<>();
List<Student> students=new ArrayList<>();
------------------------------------------
Student student01=new Student();
student01.setStudentName("student01");
students.add(student01);
Course course01=new Course();
course01.setCourseName("Course01");
courses.add(course01);
student01.setCourses(courses);
course01.setStudents(students);
this.courseDao.save(course01);
this.studentDao.save(student01);
-------------------------------------------------------------------------------------------------------------------------------------------------------
这时候我加入了两个1.mappedBy,用在被维护端,这里的course作为被维护端,student作为维护端,这里只要加了mappedBy的那一端,就意味着放弃了对关系的维护,也不能增删改查中间表的关系。
inverse是控制反转,加了就意味着放弃维护关系的权力。下面是我创建成功的表的截图
在这里用jointable,测试还是上面的方法,没有动,然后就student和course都成功了
这时候你也可以指定在jointable中name指定表名,然后指定外键,joinColumns是主操作表的中间表列,可以有好几个,而inverseJoinColumns是副操作表的中间表列,也就是student作为主操作表,
然后course作为子操作表,我尝试改过@JoinColumn(name="student_ID")变成@JoinColumn(name="student_ID~"),然后中间表就生不成了。
下面是生成上表的代码
course:
@ManyToMany(cascade = CascadeType.REMOVE,fetch = FetchType.EAGER,mappedBy="courses") private List<Student> students;
student:
@ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.REMOVE) @JoinTable(name="students_course"//指定中间的名字是students_course,如果不写也会默认生成,格式是主控方+被控方,或者是joinTable在哪里,就是谁在前 //joinColumns={@JoinColumn(name="student_ID")},//这个是用来指定中间表的主键的,如果不指定,默认为双方表的主键 //inverseJoinColumns={@JoinColumn(name="course_ID")}//这个是用来指定中间表的放弃控制的表的,inverse嘛,就是控制反转,放弃控制权限。因为这里已经用了mappedBy,已经放弃了 )
下面的代码是用来测试被放弃的一端能不能操纵数据库的片段代码,发现并不能删除中间表的关系
test:
Course course01=this.courseDao.findByCourse_name("Course01"); List<Student> students=course01.getStudents(); for (int i=0;i<students.size();i++){ if (students.get(i).getStudentName().equals("student02")){ students.remove(students.get(i)); course01.setStudents(students); this.courseDao.save(course01); System.out.println("删除关系成功"); }else { System.out.println("不存在关系"); } }
上面是指定了student作为主控方的,我们来测试一下,下面是测试代码和测试结果,中间表中的一个关系被删除了
操作方法是,先找到这个学生,然后得到他的选课list,然后move掉list中的某个,再进行保存
Student student01=this.studentDao.findByStudent_name("student01"); List<Course> courses=student01.getCourses(); for (int i=0;i<courses.size();i++){ if(courses.get(i).getCourseName().equals("course02")){ courses.remove(courses.get(i)); student01.setCourses(courses); this.studentDao.save(student01); System.out.println("删除关系成功"); }else { System.out.println("不存在关系"); } }
-----------------------------
student:
@ManyToMany()
@JoinTable()//创建了中间表
private List<Course> courses;
-----------------------------
course:
@ManyToMany(mappedBy = "courses")//被维护端
--------------------------------------------------------------------------------------------------------------------------------------------------------
在之后我在测试中,注释了student的保存的save方法,留着course的save,于是可以运行成功,course保存,然后student还是空表,当然student-cours也是空表,数据库的默认中间表是这样的,
谁作为维护,谁在前,比如这里的中间表是student。
this.courseDao.save(course01);
this.courseDao.save(course02);
// this.studentDao.save(student01);
// this.studentDao.save(student02);
-------------------------------------------------------------------------------------------------------------------------------------------------------
然后就是增删改查的操作了,findByStudent_name是我自己写的一个@Query函数,可以用id查。emm~写个删吧,
这是删中间表的关系,注意这里要设置成@ManyToMany(fetch = FetchType.EAGER),不然会爆出
session错误。因为这个已经跨表了
1.这个是删除对象,
2.删除被控方course:因为我上面设计的时候设计了级联,student和course都设置了REMOVE。额,,,我也不知我为什么设置,你删我,我删你,按照下面的代码做下去的时候,就全部删除了。course和student,
中间表都删除空了...
Course course01=this.courseDao.findByCourse_name("Course01"); this.courseDao.delete(course01); System.out.println("删除course01成功");
3.在我把级联去掉后,我试着删除了student01,发现student中的student01被删除,然后中间表中的关系被删除,
// Student studen01=this.studentDao.findByStudent_name("student01"); // this.studentDao.delete(studen01); // System.out.println("删除student01成功");
4.我在尝试一下删除course,发现可以删除成功,但是中间表没有进行更新修改,这就出问题了,课都没了,学生还在选,那可不行
Course course01=this.courseDao.findByCourse_name("Course01"); this.courseDao.delete(course01); System.out.println("删除course01成功");
--------------------------------
// Course course01=this.courseDao.findByCourse_name("Course01");//这段代码是错误的,因为course已经设置为被维护端,不会维护,所以在操纵数据库的时候就只会
//进行查询,而不能进行修改。 // List<Student> students=course01.getStudents(); // for (int i=0;i<students.size();i++){ // if (students.get(i).getStudentName().equals("student02")){ // students.remove(students.get(i)); // course01.setStudents(students); // this.courseDao.save(course01); // System.out.println("删除关系成功"); // }else { // System.out.println("不存在关系"); // } // }
Student student01=this.studentDao.findByStudent_name("student01");//这段代码是正确的,因为students是维护方,如果进行删除操作,就会出现查询,删除操作,和保存。
List<Course> courses=student01.getCourses();
for (int i=0;i<courses.size();i++){
if(courses.get(i).getCourseName().equals("Course01")){
courses.remove(courses.get(i));
student01.setCourses(courses);
this.studentDao.save(student01);
System.out.println("删除关系成功");
}else {
System.out.println("不存在关系");
}
}
-------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------
多对多的关系是靠第三表来维护的,所以不建议执行级联删除操作,可以直接修改第三表实现,比如student和course,
如果出现学生不选这门课程了,或者学生修改了其他的,是对中间表的操作,比如这里有俩学生,俩课程。如果是全选,也就是
2*2=4,中间表中有四条记录,那么修改的时候就只需要增删改查中间表就可以修改学生对应的选课。下面有个增的例子
增------------------------
List<Student> students=new ArrayList<>(); Student student01=new Student(); student01.setStudentName("student01"); students.add(student01); Course course01=this.courseDao.findByCourse_name("Course01"); List<Course> courses=new ArrayList<>(); courses.add(course01); course01.setStudents(students); student01.setCourses(courses); this.courseDao.save(course01); this.studentDao.save(student01); System.out.println("HHHHHHHHHH新建成功");
-------------------------------------------------------------------------------------------------------------------------------------------------------
如果不喜欢这个,可以进行级联保存,
放弃外键维护权:比如student和course,如果course放弃外键维护权,即不维护外键,也就是如果设置了student上设置级联保存,那么在新建student并且new了course放置在student里,进行
这时候保存student,student和course都会被保存,但这时course里的外键会是空。
(来源)https://blog.csdn.net/jjizh/article/details/76449334
-------------------------------------------------------------------------------------------------------------------------------------------------------
@JoinTable参数详解:
(来源)https://blog.csdn.net/jiangyu1013/article/details/79503581