一对多关联映射(one-to-many单向)
一、对象模型
在对象模型中一对多关联映射,通常在一的一端要使用集合来表示关联关系。
比如班级-->学生就是一个典型的一对多的关系。
说明:之所以在一的一端需要持有对多的一端集合引用,是为了方便在加载班级的时候同时加载对应班级
下面的学生。
二、原理
【实现原理】先有班级[one]后有学生[many],实现一对多需要在多的一端建立外键指向一的端
one-to-many单向:就是能从
Cleasses中访问到对应的学生,反之无效.
三、面试题
【面试题】多对一与一对多的区别与联系?
一对多关联映射利用了多对一关联映射原理
多对一关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是多指向一(Person---Group)
一对多关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是一指向多(Classes---Students)
总结:也就是说一对多和多对一的映射策略是一样的,只是角度不同
四、业务模型(实体类)
ClassesPOJO.java
private int id;
private String name;
private Set students;
//一指向多
StudentPOJO.java
private int id;
private String name;
五、配置文件
(1)一的一端(Classes.hbm.xml)的配置:需要加入<set>标签
<set name="students(Classes实体类中 Set接口类型的变量名 )">
<key column="classid(对应于多的一端在数据库中的外键 )"/>
<one-to-many class="多的一端实体类的路径 ">
</set>
<hibernate-mapping package="com.tarena.hibernate">
<class name="Classes" table="t_classes">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="students">
<key column="classesid"/>
<one-to-many class="Student"/>
</set>
</class>
</hibernate-mapping>
(2)多的一端(Student.hbm.xml)的配置:
<hibernate-mapping>
<class name="com.bjsxt.tarena.Student" table="t_student">
<id name="id">
<generator class="native"/>//ID自动增长
</id>
<property name="name"/>
</class>
</hibernate-mapping>
六、代码测试
(1)save操作()
存储策略:因为在 Classes实体类(One)中存在一个对 Student实体类的 Set集合引用,所以要先存
Student对象,然后在为 Student分配班级。
public void testSave1() {
Session session = null;
try
{
session = HibernateUtils.getSession();
session.beginTransaction();
//创建学生1
Student student1 = new Student();
student1.setName("张三" );
session.save(student1);//记得要先保存学生信息到数据库
//创建学生2
Student student2 = new Student();
student2.setName("李四" );
session.save(student2);//记得要先保存学生信息到数据库
//将学生放入Classes实体的Set集合
Set students = new HashSet();
students.add(student1);
students.add(student2);
//创建学生对象并把学生集合在放入到班级对象中
Classes classes = new Classes();
classes.setName("sd0801");//为班级分配学生
classes.setStudents(students);
//保存班级对象的同时也把对应班级的学生存入数据库
session.save(classes);
session.getTransaction().commit();
}catch(Exception e)
{
e.printStackTrace();
session.getTransaction().rollback();
}finally
{
HibernateUtils.closeSession(session);
}
}
说明:(1)上面方法之所以可以正确保存学生对象,是因为 t_students表中的外键 classesid约束允许为空,
假如此外键设置非空了将会插入数据库的时候报错。
(2)上面代码的插入数据库的流程:先存入学生对象(id自增,name程序设定,classesid为空),当创建完班
级对象后,在象数据库存入班级信息的时候在同时把班级的 id做为外键同时也为 t_students表的 classesid
存入对应班级的 id(此时 Hibernate会发出 update语句),由此可得出结论:假如同时批量创建学生对象,然
后为多个学生分配班级,此时 hibernate会发出多个 update语句去为 t_students表外键 classesid赋值,所
以影像了程序的性能。
(2)Load操作(加载班级看对应班级的学生信息)
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);
}
}
在一一端维护关系的缺点:
*
如果将 t_student表里的 classesid字段设置为非空,则无法保存
*
因为不是在 student这一端维护关系,所以 student不知道是哪个班的,所以需要发出多余的
update语句来更新关系(所以建议采用下面的双向关联一对多)一对多关联映射(one-to-many双向(常用))
一、如何实现一对多双向(实现原理)
(1)在一的一端的实体类中加入对多的一端的 Set接口引用
如:
private Set students;
//一指向多(Set)
(2)在多的一端的实体类中加入对一的一端的实体引用
如:
private Classes classid;
//多指向一(vo)
对象模型实例图如下:
二、实体类映射的配置文件
Classess.hb.xml:
<hibernate-mapping>
<class name="shuangXiang.pojo.Classess" table="t_classes">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<set name="students" inverse="true" cascade="all">
<!--对应Classes实体类中private Set students; -->
<key column="classid"/><!--对应于多的一端在数据库中的外键-->
<one-to-many class="shuangXiang.pojo.Student"/>
<!--多的一端实体类的路径-->
</set>
</class>
</hibernate-mapping>
Student.hb.xml:
<hibernate-mapping>
<class name="shuangXiang.pojo.Student" table="t_student">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="name" column="name" type="java.lang.String"/>
<many-to-one name="classess" column="classid"></many-to-one>
<!--many-to-one会用name属性的值来生成一个外键指向一的一端,维护多对一的关系,但是在一对多双
向关联的时候必须用column属性把name属性的值绑定在一起(此时classid对应于Classes.hbm.xml文件中<key
column="classid"/>;)
-->
</class>
</hibernate-mapping>
三、代码测试
public void testSave02(){
session = HibernateUtils.getSession();
session.beginTransaction();
//先创建好班级
Classess classess = new Classess();
classess.setName("0802");
session.save(classess);
//按需要给对应的学生分配班级
Student student = new Student();
student.setName("李四" );
student.setClassess(classess);
session.save(student);
session.getTransaction().commit();
}
public void testLoad(){
Session session = HibernateUtils.getSession();
Student stu = (Student)session.load(Student.class,4);
System.out.println("单向获取的姓名:" +stu.getName());
System.out.println("单向获取的班级:" +stu.getClassess().getName());
Classess classes = (Classess)session.load(Classess.class,4);
System.out.println("获取班级姓名:" +classes.getName());
}
四.相关总结
(1)hihernate一对多关联映射(双向 Classes<----->Student)
(2)一对多双向关联映射:
*在一一端的集合上使用<key>,在对方表中加入一个外键指向一一端
*在多一端采用<many-to-one>
注意:<key>标签指定的外键字段必须和<many-to-one>指定的外键字段一致,否则引用字段的错误
如果在”一“一端维护一对多关联关系,hibernate会发出多余的 udpate语句,所以我们一般在多的一端来维护关联关系
(3)关于 inverse属性:
inverse主要用在一对多和多对多双向关联上,inverse可以被设置到集合标签<set>上,
默认 inverse为 false,所以我们可以从”一“一端和”多“一端维护关联关系,
如果设置成 inverse为 true,则我们只能从多一端来维护关联关系
注意:inverse属性,只影响数据的存储,也就是持久化
(4)inverse和 cascade
*inverse是关联关系的控制方向
*cascade操作上的连锁反应