Hibernate(六)一对多映射(多对一)
一、Hinbernate中持久化类的关联关系
在数据库中,表表之间是通过外键关联的,在程序中是要转化为持久化类也就是(JAVA Bean)来实例的。
但在Hibernater中持久化的之间的映射关系,不是通外键建立关联,而是通过属性.主要有以下几种
- 一对一,
- 一对多(多对一)
- 多对多
关联方向:
- 单向关联
- 双向关联
二、一对多单向关联关系
2.1、建立数据库表
班级表,和学生表,学生生通过班级表中的,班级编号为外键
--班级表 create table grade ( gid number primary key, --班级ID gname varchar2(50), --班级名称 gdesc varchar2(50) --班级介绍 ); --学生表 create table student ( sid number primary key, --主键ID学生ID sname varchar2(20), --学生姓名 sex varchar2(20), --学生性别 gid number references grade(gid) ---外键班级ID );
2.2、建立持久化类和映射配置文件
班级和学生类
package entity; import java.util.HashSet; import java.util.Set; /* * 班级类 */ public class Grade implements java.io.Serializable { // Fields private static final long serialVersionUID = 1L; private int gid; private String gname; private String gdesc; private Set<Student> students = new HashSet<Student> (); // Constructors /** default constructor */ public Grade() { } /** minimal constructor */ public Grade(int gid) { this.gid = gid; } /** full constructor */ public Grade(int gid, String gname, String gdesc, Set<Student> students) { this.gid = gid; this.gname = gname; this.gdesc = gdesc; this.students = students; } // Property accessors public int getGid() { return this.gid; } public void setGid(int gid) { this.gid = gid; } public String getGname() { return this.gname; } public void setGname(String gname) { this.gname = gname; } public String getGdesc() { return this.gdesc; } public void setGdesc(String gdesc) { this.gdesc = gdesc; } public Set<Student> getStudents() { return this.students; } public void setStudents(Set<Student> students) { this.students = students; } }
学生类
package entity; /* * 学生类 */ public class Student implements java.io.Serializable { // Fields private static final long serialVersionUID = 1L; private int sid; private String sname; private String sex; // Constructors /** default constructor */ public Student() { } /** minimal constructor */ public Student(int sid) { this.sid = sid; } /** full constructor */ public Student(int sid, String sname, String sex ) { this.sid = sid; this.sname = sname; this.sex = sex; } // Property accessors public int getSid() { return this.sid; } public void setSid(int sid) { this.sid = sid; } public String getSname() { return this.sname; } public void setSname(String sname) { this.sname = sname; } public String getSex() { return this.sex; } public void setSex(String sex) { this.sex = sex; } }
hibernate.cf.xml和班级、学生的配置文件
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="dialect"> org.hibernate.dialect.Oracle9Dialect </property> <property name="connection.url"> jdbc:oracle:thin:@localhost:1521:orcl </property> <property name="connection.username">root</property> <property name="connection.password">root</property> <property name="connection.driver_class"> oracle.jdbc.OracleDriver </property> <property name="show_sql">true</property> <property name="format_sql">true</property> <mapping resource="entity/Grade.hbm.xml" /> <mapping resource="entity/Student.hbm.xml" /> </session-factory> </hibernate-configuration>
持久类配置先来学生类
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="entity.Student" table="STUDENT" schema="ROOT"> <id name="sid" type="java.lang.Integer"> <column name="SID" precision="22" scale="0" /> <generator class="assigned" /> </id> <property name="sname" type="java.lang.String"> <column name="SNAME" length="20" /> </property> <property name="sex" type="java.lang.String"> <column name="SEX" length="20" /> </property> </class> </hibernate-mapping>
班级类配置
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="entity.Grade" table="GRADE" schema="ROOT"> <id name="gid" type="java.lang.Integer"> <column name="GID" precision="22" scale="0" /> <generator class="assigned" /> </id> <property name="gname" type="java.lang.String"> <column name="GNAME" length="50" /> </property> <property name="gdesc" type="java.lang.String"> <column name="GDESC" length="50" /> </property> <!--建立set属性,也可以建立list和持久化类中一致就行 --> <set name="students"> <key> <!--这里的列是指学生表中的班级编号 --> <column name="GID" precision="22" scale="0" /> </key> <!-- 通过class属性指定set的属性 --> <one-to-many class="entity.Student" /> </set> </class> </hibernate-mapping>
测试类
package Test; import java.util.Set; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import entity.Grade; import entity.Student; public class Demo1 { /** * 测试类 */ public static void main(String[] args) { save(); find(); update(); } public static void save() { // 声明班级对象,并赋值 Grade grade = new Grade(); grade.setGid(201504); grade.setGname("Java一班"); grade.setGdesc("刚开始学习JAVA"); // 声明2个学生对象 Student stu1 = new Student(); stu1.setSid(201504012); stu1.setSname("张三"); stu1.setSex("男"); Student stu2 = new Student(); stu2.setSid(201504013); stu2.setSname("李四"); stu2.setSex("女"); // 将学生添加到班级 grade.getStudents().add(stu1); grade.getStudents().add(stu2); // 建立session Session session = new Configuration().configure().buildSessionFactory() .openSession(); // 开始事务 Transaction transaction = session.beginTransaction(); // 保存班级 session.save(grade); // 保存学生 session.save(stu1); session.save(stu2); // 提交事务 transaction.commit(); // 关闭session session.close(); } /* * 将基中一个学生更改为别一个班级 */ public static void update() { // 声明班级对象,并赋值 Grade grade = new Grade(); grade.setGid(201506); grade.setGname("Java二班"); grade.setGdesc("学习JAVA二年级"); // 获取一个学生的信息 // 建立session Session session = new Configuration().configure().buildSessionFactory() .openSession(); // 开始事务 Transaction transaction = session.beginTransaction(); Student stu1=(Student) session.get(Student.class, 201504013); //将学生添加到这个新的班级 grade.getStudents().add(stu1); // 保存班级 session.save(grade); // 保存学生 session.save(stu1); // 提交事务 transaction.commit(); // 关闭session session.close(); } // 查询班级 public static void find() { // 建立session Session session = new Configuration().configure().buildSessionFactory() .openSession(); Grade g = (Grade) session.get(Grade.class, 201504); System.out.println("班级信息:" + g.getGid() + "\t" + g.getGname() + g.getGdesc()); // 通过班级获取这个班级的学生信息 System.out.println("201504班的学生信息如下:"); Set<Student> set = g.getStudents(); for (Student stu : set) { System.out.println(stu.getSid() + "\t" + stu.getSname() + "\t" + stu.getSex()); } } }
以上只上单向关联,一般都会有双向关联
同样要在学生类中添加Grade 属性
在学生类的配置文件中配置关联字段
二、建立双向关联
2.1、更改学生类和配置文件
package entity; /* * 学生类 */ public class Student implements java.io.Serializable { // Fields private static final long serialVersionUID = 1L; private int sid; private String sname; private String sex; //增加班级属性 private Grade grade; // Constructors /** default constructor */ public Student() { } /** minimal constructor */ public Student(int sid) { this.sid = sid; } /** full constructor */ public Student(int sid, String sname, String sex ) { this.sid = sid; this.sname = sname; this.sex = sex; } // Property accessors public int getSid() { return this.sid; } public void setSid(int sid) { this.sid = sid; } public String getSname() { return this.sname; } public void setSname(String sname) { this.sname = sname; } public String getSex() { return this.sex; } public void setSex(String sex) { this.sex = sex; } public Grade getGrade() { return grade; } public void setGrade(Grade grade) { this.grade = grade; } }
配置文件
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="entity.Student" table="STUDENT" schema="ROOT"> <id name="sid" type="java.lang.Integer"> <column name="SID" precision="22" scale="0" /> <generator class="assigned" /> </id> <property name="sname" type="java.lang.String"> <column name="SNAME" length="20" /> </property> <property name="sex" type="java.lang.String"> <column name="SEX" length="20" /> </property> <!--配置grade属性 --> <many-to-one name="grade" class="entity.Grade"> <!--指定学生表中的外键 --> <column name="GID" /> </many-to-one> </class> </hibernate-mapping>
2.2、测试类
建立双向关联后,就可以通过学生来获取班级信息
清空数据表中的数据
package Test; import org.hibernate.Session; import org.hibernate.cfg.Configuration; import entity.Student; public class Demo2 { /** * 测试类 */ public static void main(String[] args) { save(); findByStu(); } public static void findByStu(){ Session session = new Configuration().configure().buildSessionFactory() .openSession(); //获取一个学生的信息 get方法为通过主键查询 Student stu=(Student) session.get(Student.class, 201504013); System.out.println("学生信息:\t"+stu.getSid()+"\t"+stu.getSname()+"\t"+stu.getSex()); //通过学生信息得到班级信息 System.out.println("这个学生的班级信息:"+stu.getGrade().getGid()+"\t"+stu.getGrade().getGname()+"\t"+stu.getGrade().getGdesc()); } } public static void save() { // 声明班级对象,并赋值 Grade grade = new Grade(); grade.setGid(201504); grade.setGname("Java一班"); grade.setGdesc("刚开始学习JAVA"); // 声明2个学生对象 Student stu1 = new Student(); stu1.setSid(201504012); stu1.setSname("张三"); stu1.setSex("男"); stu1.setGrade(grade); Student stu2 = new Student(); stu2.setSid(201504013); stu2.setSname("李四"); stu2.setSex("女"); stu2.setGrade(grade); // 将学生添加到班级 /*grade.getStudents().add(stu1); grade.getStudents().add(stu2);*/ // 建立session Session session = new Configuration().configure().buildSessionFactory() .openSession(); // 开始事务 Transaction transaction = session.beginTransaction(); // 保存班级 session.save(grade); // 保存学生 session.save(stu1); session.save(stu2); // 提交事务 transaction.commit(); // 关闭session session.close(); }
三、级联保存和删除
能不能只保存学生时,同时保存班级呢,或者保存班级时,同时保存学生呢
3.1、cascade属性
保存班级时,同时保存学生信息
修改班级配置,在set标签中添加cascade属性,设置为save-update
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="entity.Grade" table="GRADE" schema="ROOT"> <id name="gid" type="java.lang.Integer"> <column name="GID" precision="22" scale="0" /> <generator class="assigned" /> </id> <property name="gname" type="java.lang.String"> <column name="GNAME" length="50" /> </property> <property name="gdesc" type="java.lang.String"> <column name="GDESC" length="50" /> </property> <!--建立set属性,也可以建立list和持久化类中一致就行 --> <set name="students" cascade="save-update"> <key> <!--这里的列是指学生表中的班级编号 --> <column name="GID" precision="22" scale="0" /> </key> <!-- 通过class属性指定set的属性 --> <one-to-many class="entity.Student" /> </set> </class> </hibernate-mapping>
测试类
package Test; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import entity.Grade; import entity.Student; public class Demo3 { /** * @param args */ public static void main(String[] args) { save(); } public static void save() { // 声明班级对象,并赋值 Grade grade = new Grade(); grade.setGid(201504); grade.setGname("Java一班"); grade.setGdesc("刚开始学习JAVA"); // 声明2个学生对象 Student stu1 = new Student(); stu1.setSid(201504012); stu1.setSname("张三"); stu1.setSex("男"); stu1.setGrade(grade); Student stu2 = new Student(); stu2.setSid(201504013); stu2.setSname("李四"); stu2.setSex("女"); stu2.setGrade(grade); // 将学生添加到班级 grade.getStudents().add(stu1); grade.getStudents().add(stu2); // 建立session Session session = new Configuration().configure().buildSessionFactory() .openSession(); // 开始事务 Transaction transaction = session.beginTransaction(); // 保存班级 session.save(grade); // 保存学生 //session.save(stu1); //session.save(stu2); // 提交事务 transaction.commit(); // 关闭session session.close(); } }
反之,在学生类的配置文件one-many中添加cascade属性设置为save-update
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="entity.Student" table="STUDENT" schema="ROOT"> <id name="sid" type="java.lang.Integer"> <column name="SID" precision="22" scale="0" /> <generator class="assigned" /> </id> <property name="sname" type="java.lang.String"> <column name="SNAME" length="20" /> </property> <property name="sex" type="java.lang.String"> <column name="SEX" length="20" /> </property> <!--配置grade属性 --> <many-to-one name="grade" class="entity.Grade" cascade="save-update"> <!--指定学生表中的外键 --> <column name="GID" /> </many-to-one> </class> </hibernate-mapping>
package Test; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import entity.Grade; import entity.Student; public class Demo3 { /** * @param args */ public static void main(String[] args) { save(); } public static void save() { // 声明班级对象,并赋值 Grade grade = new Grade(); grade.setGid(201504); grade.setGname("Java一班"); grade.setGdesc("刚开始学习JAVA"); // 声明2个学生对象 Student stu1 = new Student(); stu1.setSid(201504012); stu1.setSname("张三"); stu1.setSex("男"); stu1.setGrade(grade); Student stu2 = new Student(); stu2.setSid(201504013); stu2.setSname("李四"); stu2.setSex("女"); stu2.setGrade(grade); // 将学生添加到班级 grade.getStudents().add(stu1); grade.getStudents().add(stu2); // 建立session Session session = new Configuration().configure().buildSessionFactory() .openSession(); // 开始事务 Transaction transaction = session.beginTransaction(); // 保存班级 //session.save(grade); // 保存学生 session.save(stu1); session.save(stu2); // 提交事务 transaction.commit(); // 关闭session session.close(); } }
3.2、inverse属性
首先在班级类中设置invers属性为false时,删除班级
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="entity.Grade" table="GRADE" schema="ROOT"> <id name="gid" type="java.lang.Integer"> <column name="GID" precision="22" scale="0" /> <generator class="assigned" /> </id> <property name="gname" type="java.lang.String"> <column name="GNAME" length="50" /> </property> <property name="gdesc" type="java.lang.String"> <column name="GDESC" length="50" /> </property> <!--建立set属性,也可以建立list和持久化类中一致就行 --> <set name="students" cascade="save-update" inverse="false"> <key> <!--这里的列是指学生表中的班级编号 --> <column name="GID" precision="22" scale="0" /> </key> <!-- 通过class属性指定set的属性 --> <one-to-many class="entity.Student" /> </set> </class> </hibernate-mapping>
测试类
package Test; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import entity.Grade; import entity.Student; public class Demo4 { /** * @param args */ public static void main(String[] args) { delete() ; } public static void delete() { // 建立session Session session = new Configuration().configure().buildSessionFactory() .openSession(); // 开始事务 Transaction transaction = session.beginTransaction(); // 保存班级 Grade grade=(Grade) session.get(Grade.class, 201504); // 保存学生 session.delete(grade); // 提交事务 transaction.commit(); // 关闭session session.close(); } }
结果:发现班级表中的班级已经删除,而学生表中数据没有删除,只是GID字段为NULL
下面将inverse设置为true时,添加新学生,和新的班级
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="entity.Grade" table="GRADE" schema="ROOT"> <id name="gid" type="java.lang.Integer"> <column name="GID" precision="22" scale="0" /> <generator class="assigned" /> </id> <property name="gname" type="java.lang.String"> <column name="GNAME" length="50" /> </property> <property name="gdesc" type="java.lang.String"> <column name="GDESC" length="50" /> </property> <!--建立set属性,也可以建立list和持久化类中一致就行 --> <set name="students" cascade="save-update" inverse="true"> <key> <!--这里的列是指学生表中的班级编号 --> <column name="GID" precision="22" scale="0" /> </key> <!-- 通过class属性指定set的属性 --> <one-to-many class="entity.Student" /> </set> </class> </hibernate-mapping>
测试类
package Test; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import entity.Grade; import entity.Student; public class Demo4 { /** * @param args */ public static void main(String[] args) { delete() ; } public static void delete() { // 声明班级对象,并赋值 Grade grade = new Grade(); grade.setGid(201509); grade.setGname("Java三班"); grade.setGdesc("ASP.NET"); // 声明2个学生对象 Student stu1 = new Student(); stu1.setSid(201509009); stu1.setSname("王五"); stu1.setSex("女"); stu1.setGrade(grade); Student stu2 = new Student(); stu2.setSid(201509045); stu2.setSname("赵六"); stu2.setSex("女"); stu2.setGrade(grade); // 建立session Session session = new Configuration().configure().buildSessionFactory() .openSession(); // 开始事务 Transaction transaction = session.beginTransaction(); // 保存学生 session.save(stu1); session.save(stu2); // 提交事务 transaction.commit(); // 关闭session session.close(); } }
结果发现,我并没有用班级添加学生,也没有保存班级,只是保存了学生,班级信息一起保存了