例:一名学生可以选多门课程,一门课程也可以被多名学生选择。
准备
POJO
// 学生:com.zze.bean.Student
package com.zze.bean;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
public class Student {
private Integer id;
private String name;
private Integer age;
private Date birthday;
private String address;
private Class clazz;
private Set<Course> courses = new HashSet<>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
public Set<Course> getCourses() {
return courses;
}
public void setCourses(Set<Course> courses) {
this.courses = courses;
}
}
// 课程:com.zze.bean.Course
package com.zze.bean;
import java.util.HashSet;
import java.util.Set;
public class Course {
private Integer id;
private String name;
private Set<Student> students = new HashSet<>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Student> getStudents() {
return students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
}
配置文件
<!-- com/zze/bean/Student.hbm.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.zze.bean.Student" table="student">
<id name="id">
<generator class="native"/>
</id>
<property name="name" length="32"/>
<property name="age"/>
<property name="birthday"/>
<property name="address"/>
<many-to-one name="clazz" column="cid" class="com.zze.bean.Class" cascade="save-update,delete"/>
<set name="courses" table="student_course">
<!--
key :
column : 当前对象类对应中间表的外键名称
-->
<key column="studentId"/>
<!--
many-to-many : 标识多对多关系
class : 对方类全路径
column : 对方对应中间表的外键名称
-->
<many-to-many class="com.zze.bean.Course" column="courseId"/>
</set>
</class>
</hibernate-mapping>
<!-- com/zze/bean/Course.hbm.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.zze.bean.Course" table="course">
<id name="id">
<generator class="native"/>
</id>
<property name="name" length="32"/>
<set name="students" table="student_course">
<!--
key :
column : 当前对象类对应中间表的外键名称
-->
<key column="courseId"/>
<!--
many-to-many : 标识多对多关系
class : 对方类全路径
column : 对方对应中间表的外键名称
-->
<many-to-many class="com.zze.bean.Student" column="studentId"/>
</set>
</class>
</hibernate-mapping>
<!-- hibernate.cfg.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://192.168.208.153:3306/test</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="current_session_context_class">thread</property>
<property name="hibernate.hbm2ddl.auto">create</property>
<mapping resource="com/zze/bean/Class.hbm.xml"></mapping>
<mapping resource="com/zze/bean/Student.hbm.xml"></mapping>
<mapping resource="com/zze/bean/Course.hbm.xml"></mapping>
</session-factory>
</hibernate-configuration>
使用
普通操作
例:两方关联,保存两方 - 新建一个学生,新建一个课程,让学生主动关联课程,也让课程主动关联学生,保存学生与课程,异常。
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student student = new Student();
student.setName("张三");
Course course = new Course();
course.setName("数学");
student.getCourses().add(course);
course.getStudents().add(student);
session.save(student);
session.save(course);
transaction.commit();
/*
抛异常:org.hibernate.exception.ConstraintViolationException: could not execute statement
*/
抛异常的原因是:多对多关系的维护是依赖第三张中间表,而中间表的字段是两个分别关联多对多这两张表的主键的外键,且这两个外键在中间表中组成了联合主键,而上述代码实际上进行了两次 insert 操作,这两次操作的内容违反了联合主键约束,所以会抛出异常。
分析原因后,可以得出结论,在多对多中两方关联时,必须有一方得放弃维护外键关系(一般是被动方放弃,即 Course 放弃),下面配置二选一:
<!-- com/zze/bean/Student.hbm.xml:修改 set 标签,添加属性 inverse="true" -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.zze.bean.Student" table="student">
<id name="id">
<generator class="native"/>
</id>
<property name="name" length="32"/>
<property name="age"/>
<property name="birthday"/>
<property name="address"/>
<many-to-one name="clazz" column="cid" class="com.zze.bean.Class" cascade="save-update,delete"/>
<set name="courses" table="student_course" inverse="true">
<!--
key :
column : 当前对象类对应中间表的外键名称
-->
<key column="studentId"/>
<!--
many-to-many : 标识多对多关系
class : 对方类全路径
column : 对方对应中间表的外键名称
-->
<many-to-many class="com.zze.bean.Course" column="courseId"/>
</set>
</class>
</hibernate-mapping>
<!-- com/zze/bean/Course.hbm.xml:修改 set 标签,添加属性 inverse="true" -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.zze.bean.Course" table="course">
<id name="id">
<generator class="native"/>
</id>
<property name="name" length="32"/>
<set name="students" table="student_course" inverse="true">
<!--
key :
column : 当前对象类对应中间表的外键名称
-->
<key column="courseId"/>
<!--
many-to-many : 标识多对多关系
class : 对方类全路径
column : 对方对应中间表的外键名称
-->
<many-to-many class="com.zze.bean.Student" column="studentId"/>
</set>
</class>
</hibernate-mapping>
修改配置后,再次执行上述示例代码,成功。
级联操作
级联保存或更新
例:一方关联,保存一方 - 新建一个学生,新建一个课程,让学生主动关联课程,保存学生,级联保存了课程,成功
<!-- com/zze/bean/Student.hbm.xml:修改 set 标签,添加属性 cascade="save-update" -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.zze.bean.Student" table="student">
<id name="id">
<generator class="native"/>
</id>
<property name="name" length="32"/>
<property name="age"/>
<property name="birthday"/>
<property name="address"/>
<many-to-one name="clazz" column="cid" class="com.zze.bean.Class" cascade="save-update,delete"/>
<set name="courses" table="student_course" cascade="save-update">
<!--
key :
column : 当前对象类对应中间表的外键名称
-->
<key column="studentId"/>
<!--
many-to-many : 标识多对多关系
class : 对方类全路径
column : 对方对应中间表的外键名称
-->
<many-to-many class="com.zze.bean.Course" column="courseId"/>
</set>
</class>
</hibernate-mapping>
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student student = new Student();
student.setName("张三");
Course course = new Course();
course.setName("数学");
student.getCourses().add(course);
session.save(student);
transaction.commit();
级联删除
例:删除学生,级联删除该学生所选的课程。
<!-- com/zze/bean/Student.hbm.xml:修改 set 标签,添加属性 cascade="delete" -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.zze.bean.Student" table="student">
<id name="id">
<generator class="native"/>
</id>
<property name="name" length="32"/>
<property name="age"/>
<property name="birthday"/>
<property name="address"/>
<many-to-one name="clazz" column="cid" class="com.zze.bean.Class" cascade="save-update,delete"/>
<set name="courses" table="student_course" cascade="save-update,delete">
<!--
key :
column : 当前对象类对应中间表的外键名称
-->
<key column="studentId"/>
<!--
many-to-many : 标识多对多关系
class : 对方类全路径
column : 对方对应中间表的外键名称
-->
<many-to-many class="com.zze.bean.Course" column="courseId"/>
</set>
</class>
</hibernate-mapping>
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student student = session.get(Student.class, 1);
session.delete(student);
transaction.commit();
评论区