Hibernate关系映射-级联操作
在双向一对多的学习中,我们发现每次保存对象时,学生对象和年纪对象都需要我们持久化之session,既然它们两者有关联关系,可不可以只持久化其中一端,另一端就会自动的被持久化呢,这个属性就是Hibernate的cascade。cascade是多对一、一对多、一对一、多对多各种关系之间的一种级联操作行为。(关键语句:cascade="save-update"。)
一、什么是级联操作
官方解释:必须级联到关联目标的操作,默认情况下没有级联操作。
当Hibernate持久化一个瞬时对象时,在默认情况下,它不会自动持久化所关联的其他临时对象,而是会抛出org.hibernate.TransientObjectException。
级联的意思是:本实体做了什么事,也要拉上另一个关联的实体,导致另一个实体跟着做事情。就是说我删除了,你也必须删除!关联目标,指的是关联的那个实体 。
二、准备案例
在明确了级联操作的概念知识以后,使用学生表和年纪表的双向一对多关系,实现级联操作行为。
学生表和年纪表的双向一对多关系学习请参考(Hibernate关系映射-双向一对多)。
cascade级联操作,默认是none不级联。
none:不级联。
save-update:保存和更新某一端数据时,另一端数据可以一起保存和更新。
delete:删除级联-不能在多的一端使用。
all:表示所有操作都级联
三、实现级联操作
1、配置多端
-
编写配置文件
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 package="com.uestc"> <class name="Student"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="age"/> <!-- 多对一 name对应的字段名称 class对应的类型 not-null不可以为空 column外键列名 foreign-key外键名称 --> <many-to-one cascade="save-update" name="grade" class="Grade" column="grade_id" foreign-key="fk_grade"/> </class> </hibernate-mapping>
2.编写Grade.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 package="com.uestc"> <class name="Grade"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <!-- 单向一对多关系 set:Grade类中的集合属性 name:集合属性名称 key:外键 column:外键列名 foreign-key:生成外键约束的名字(可以不写,默认随机) not-null="true" 不可以为空(可以不写,默认false) one-to-many:Grade类中属性students所表示类型 --> <set name="students" cascade="save-update"> <key foreign-key="fk_grade" column="grade_id"></key> <one-to-many class="Student"/> </set> </class> </hibernate-mapping>
-
Student类
public class Student implements Serializable { private Integer id; private String name; private Integer age; private Grade grade; @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", grade=" + grade + '}'; } 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 Grade getGrade() { return grade; } public void setGrade(Grade grade) { this.grade = grade; } }
-
Grade类
public class Grade implements Serializable { private Integer id; private String name; private Set<Student> students = new HashSet<>(); @Override public String toString() { return "Grade{" + "id=" + id + ", name='" + name + '\'' + ", students=" + students + '}'; } 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; } }
-
编写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> <!-- 初始化JDBC连接 --> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql:///hibernate</property> <property name="connection.username">root</property> <property name="connection.password">root</property> <!-- 方言 --> <property name="dialect">org.hibernate.dialect.MySQL8Dialect</property> <!-- 数据库生成方式 --> <property name="hbm2ddl.auto">update</property> <!-- validate检测,create-drop删了重新创建,create重新建表,update表如果存在插数据,如果不存在建表插数据 --> <!-- 打印sql --> <property name="show_sql">true</property> <!-- 格式化sql --> <property name="format_sql">true</property> <!-- 关联对象配置文件 --> <mapping resource="com/uestc/Grade.hbm.xml"/> <mapping resource="com/uestc/Student.hbm.xml"/> </session-factory> </hibernate-configuration>
-
测试类
/** * 单向多对一 */ @Test public void testSingleManyToOne() { Session session = null; Transaction tx = null; try { session = HibernateUtil.getSession(); tx = session.beginTransaction(); //创建实例对象 Grade grade1 = new Grade(); grade1.setName("基础"); Grade grade2 = new Grade(); grade2.setName("中级"); Student student = new Student(); student.setName("张三"); student.setAge(18); student.setGrade(grade1); Student student2 = new Student(); student2.setName("李四"); student2.setAge(18); student2.setGrade(grade1); Student student3 = new Student(); student3.setName("王五"); student3.setAge(18); student3.setGrade(grade2); //存储的顺序是根据外键约束而定的,如果外键不可以为空,必须先存储外键的一端 //如果外键可以为空,随意存储,但是建议先存储外键的一端,因为会多执行update // session.save(grade1); // session.save(grade2); session.save(student); session.save(student2); session.save(student3); tx.commit(); }catch (Exception e){ e.printStackTrace(); tx.rollback(); }finally { HibernateUtil.closeSession(); } } /** * 获取 * 查询不用开启事务,会降低性能 */ @Test public void testSingleGetManyToOne() { Session session = null; Transaction tx = null; try { session = HibernateUtil.getSession(); tx = session.beginTransaction(); Student student = session.get(Student.class,1); System.out.println("stuName:" +student.getName() + ",grade:" +student.getGrade().getName()); tx.commit(); }catch (Exception e){ e.printStackTrace(); tx.rollback(); }finally { HibernateUtil.closeSession(); } } /** * 单向一对多 */ @Test public void testSingleOneToMany() { Session session = null; Transaction tx = null; try { session = HibernateUtil.getSession(); tx = session.beginTransaction(); //创建实例对象 Student student = new Student(); student.setName("张三"); student.setAge(18); Student student2 = new Student(); student2.setName("李四"); student2.setAge(18); Student student3 = new Student(); student3.setName("王五"); student3.setAge(18); Grade grade1 = new Grade(); grade1.setName("基础"); grade1.getStudents().add(student); grade1.getStudents().add(student2); Grade grade2 = new Grade(); grade2.setName("中级"); grade2.getStudents().add(student3); //存储的顺序是根据外键约束而定的,如果外键不可以为空,必须先存储外键的一端 //单向一对多会多执行update语句,效率不如多对一 session.save(grade1); session.save(grade2); // session.save(student); // session.save(student2); // session.save(student3); tx.commit(); }catch (Exception e){ e.printStackTrace(); tx.rollback(); }finally { HibernateUtil.closeSession(); } } /** * 获取 * 查询不用开启事务,会降低性能 */ @Test public void testSingleGetOneToMany() { Session session = null; Transaction tx = null; try { session = HibernateUtil.getSession(); tx = session.beginTransaction(); Student student = session.get(Student.class,1); System.out.println("stuName:" +student.getName() + ",grade:" +student.getGrade().getName()); System.out.println("---------------------分割线-------------------"); Grade grade = session.get(Grade.class,1); System.out.println("grade:" +grade.getName()); for(Student stu : grade.getStudents()){ System.out.println("stuName:" +stu.getName()); } tx.commit(); }catch (Exception e){ e.printStackTrace(); tx.rollback(); }finally { HibernateUtil.closeSession(); } }
-
-
-
四、总结
大家可以尝试除了新增以外的其他操作,可以得出以下结论
-
在多对一的关系中,多的一端不能操作级联为delete,一般在多的一端设为save-update;
-
在一对多的关系中,如果一的一端设为delete相关配置时,多的一端不能指明外键为非空。
浙公网安备 33010602011771号