【Hibernate】一对多映射

    xiaoxiao2021-03-26  12

            举个例子,一个班级和班里学生的关系可以用如下这幅关系图表示:

                         

             从Classes的角度来看,1个班级里有多个学生,关联关系为1:n,从对象到表结构的映射关系则为:

                       

                注意:在student表中有一个classid字段,原理:先向学生表中插入学生基本信息,执行insert操作,classid字段为空,然后向class表中插入数据,同时update student这张表,当然只要进行update操作的one-to-many操作,绝对是单向的一对多关联映射,下面介绍单向一对多映射:

    单向one-to-many映射:

       一、代码搭建映射

           表结构原理图:                            实质上,同多对一映射的原理,将外键加入到多的一端,仅仅看问题的角度不同。        实体类:    (1)Student public class Student { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }     (2)Classes public class Classes { private int id; private String name; //一对多在one端,加入set集合 private Set students; public Set getStudents() { return students; } public void setStudents(Set students) { this.students = students; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }       可见,在对应的实体类当中,classes类中加入set集合,切记不是HashSet,因为Set支持延迟加载。     xml配置映射文件 <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.bjpowernode.hibernate.Classes" table="t_classes"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <set name="students"> <key column="classesid"/> <!-- 关系维护在one的这端 --> <one-to-many class="com.bjpowernode.hibernate.Sutdent"/> </set> </class> </hibernate-mapping>        如上,仅仅是对one端的classes类进行映射配置,除去"id"和"name"之外,观察<set>标签 : <set name="students"> <key column="classesid"/> <!-- 关系维护在one的这端 --> <one-to-many class="com.bjpowernode.hibernate.Sutdent"/> </set>        name属性说明了要关联的表结构为stud ents,其中子标签<key>作为维护关系的”要害”,即将名为“classesid”的字段存到了students表结构下,成为其外键!!!<one-to-many>标签,则关联了实体中的Student类,这都是套路,找到并总结出规律记住即可。     至此,单向"one-to-many"映射的结构已经实现。

       二、实现one-to-many存储与加载

    public class One2ManyTest extends TestCase{ public void testSave1(){ Session session = null; try{ session = HibernateUtils.getSession(); session.beginTransaction(); Student student1 = new Student(); Student student2 = new Student(); student1.setName("张三"); student2.setName("李四"); //只有先分别save了studnet1和2,再save class的时候,不会报错 session.save(student1); session.save(student2); Classes classes = new Classes(); classes.setName("提高班"); Set students = new HashSet(); students.add(student1); students.add(student2); classes.setStudents(students); //可以成功的保存数据,但是会发出多余的update语句,来维持关系 session.save(classes); session.getTransaction().commit(); }catch(Exception e){ e.printStackTrace(); session.getTransaction().rollback(); }finally{ HibernateUtils.closeSession(session); } } }        如上,是对该one-to-many结构实现存储的方法,因为从one端,即classes的角度进行维护,需要先将两个student进行session.save()保存, 否则TransientObjectException提示就会报出,执行代码,成功向Students表和Classes表中插入了对应数据。 public void testLoad1(){ Session session = null; try{ session = HibernateUtils.getSession(); session.beginTransaction(); Classes classes = (Classes)session.load(Classes.class, 1); System.out.println("Classes.name=" + classes.getName()); Set students = classes.getStudents(); for(Iterator iter = students.iterator(); iter.hasNext();){ Student student = (Student)iter.next(); System.out.println("student.name=" + student.getName()); } session.getTransaction().commit(); }catch(Exception e){ e.printStackTrace(); session.getTransaction().rollback(); }finally{ HibernateUtils.closeSession(session); } }         如上,从Class的角度取出id为1的数据,通过classes.getStudents()获取对应的Student信息,并通过迭代器将其进行打印。     缺陷:     1、因为Students端不知道Classes端的存在,在保存Student的时候关系字段classesid是为null的,如果将该关系字段设置为非空,则将无法保存数据。     2、因为Student不维护关系,而Classes维护关系,Classes就会发出多余的update语句,保证Classes和Student有关系,这样加载Classes的时候才可以把该Classes对应的学生加载上来。

    双向one-to-many映射:

        为了避免单向one-to-many关联映射的缺陷,使用双向关联可以解决该问题,如图:               即从classes能够拿到students,从students可以反向拿到classes    (1)修改studnet的配置文件 <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.bjpowernode.hibernate.Classes" table="t_classes"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <!-- 通过别名,使得实体中映射的classes与one对many的表字典classesid一致 --> <many-to-one name="classes" column="classesid"/> </class> </hibernate-mapping>        不要奇怪,这里出现了一个<many-to-one>标签,因为是从反向进行观察,想要双向打通,就要这样做,而且column别名的设置,要和在one端设置的别名一样,此处设置为“classesid”。     另外,对于one端可以进行翻转设置,如下: <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.bjpowernode.hibernate.Classes" table="t_classes"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <!-- 添加inverse=“true”之后,one端将不再维护关系 --> <set name="students" inverse="true"> <key column="classesid"/> <!-- 关系维护在one的这 --> <one-to-many class="com.bjpowernode.hibernate.Sutdent"/> </set> </class> </hibernate-mapping>        set标签中"inverse"属性设置为"true"之后,one端将不再维护关系,此时对于存储方法就要这样子写: public void testSave2(){ Session session = null; try{ session = HibernateUtils.getSession(); session.beginTransaction(); Classes classes = new Classes(); classes.setName("提高班"); session.save(classes); Student student1 = new Student(); Student student2 = new Student(); student1.setName("张三"); student2.setName("李四"); //从students端的角度来维护 session.save(student1); session.save(student2); session.getTransaction().commit(); }catch(Exception e){ e.printStackTrace(); session.getTransaction().rollback(); }finally{ HibernateUtils.closeSession(session); } }      此时,维护关系的一端即为many端,session.save(classes)的时候,不会向Students表中添加classesid记录了,即从one端执行save方法,student表中,classid字段此时为null值。

    对比one-to-many与many-to-one

        从维护关系上来看:         多对一维护关系:多指向一的关系,如果维护了多指向一的关系,那么加载多的时候会把一加载上来。     一对多维护关系:一指向多的关系,如果维护了一指向多的关系,那么加载一的时候会把多加载上来。      That's all.   

    转载请注明原文地址: https://ju.6miu.com/read-650110.html

    最新回复(0)