hibernate--关联映射(一对多)

时间:2021-01-02 07:43:21

在对象模型中,一对多的关联关系,使用集合来表示。

实例场景:班级对学生;Classes(班级)和Student(学生)之间是一对多的关系。

对象模型:

hibernate--关联映射(一对多)

多对一、一对多的区别:

多对一关联映射:在多的一端加入一个外键指向一对一端,它维护的关系是多指向一的。

一对多关联映射:在多的一端加入一个外键指向一对一端,它维护的关系是一指向多的。

两者使用的策略是一样的,只是各自所站的角度不同。

Classes实体类:

public class Classes {
private int id;
private String name;
}

Students实体类:

public class Student {
private int id;
private String name; //一对多通常使用Set来映射,Set是不可重复内容。
//注意使用Set这个接口,不要使用HashSet,因为hibernate有延迟加载,
private Set students;
}

Student映射文件:

<?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 package="h.one.bean">
<class name="Student" table="t_student">
<id name="id" column="id">
<generator class="native" />
</id>
<property name="name" column="name" />
</class>
</hibernate-mapping>

Classes映射文件:

<hibernate-mapping package="h.one.bean">
<class name="Classes" table="t_classess">
<id name="id" column="id">
<generator class="native" />
</id>
<property name="name" column="name" />
<!--<set>标签 映射一对多(映射set集合),name="属性集合名称",然后在用<key>标签,在多的一端加入一个外键(column属性指定列名称)指向一的一端,再采用<one-to-many>标签说明一对多,还指定<set>标签中name="students"这个集合中的类型要使用完整的类路径(例如:class="com.wjt276.hibernate.Student") -->
<set name="students">
<key column="classesid" />
<one-to-many class="com.wjt276.hibernate.Student" />
</set>
</class>
</hibernate-mapping>

一对多 单向存储实例:

session = HibernateUtil.getSession();
tx = session.beginTransaction(); Student student1 = new Student();
student1.setName("10");
session.save(student1);// 必需先存储,否则在保存classess时出错. Student student2 = new Student();
student2.setName("祖儿");
session.save(student2);// 必需先存储,否则在保存classess时出错. Set<Student> students = new HashSet<Student>();
students.add(student1);
students.add(student2); Classes classes = new Classes();
classes.setName("wjt276");
classes.setStudents(students); session.save(classes); // 提交事务
tx.commit();

一对多,在一的一端维护关系的缺点:

因为是在一的一端维护关系,这样会发出多余的更新语句,这样在批量数据时,效率不高。

还有一个,当在多的一端的那个外键设置为非空时,则在添加多的一端数据时会发生错误,数据存储不成功。

一对多 单向数据加载:

session = HibernateUtil.getSession();
tx = session.beginTransaction(); Classes classes = (Classes) session.load(Classes.class, 2);
System.out.println("classes.name=" + classes.getName());
Set<Student> students = classes.getStudents();
for (Iterator<Student> iter = students.iterator(); iter.hasNext();) {
Student student = iter.next();
System.out.println(student.getName());
}
// 提交事务
tx.commit();

一对多关联映射 双向(one-to-many)

是加载学生时,能够把班级加载上来。当然加载班级也可以把学生加载上来

1、 在学生对象模型中,要持有班级的引用,并修改学生映射文件就可以了。。

2、 存储没有变化

3、 关系模型也没有变化

学生映射文件修改后的:
<hibernate-mapping>
<class name="h.one.bean.Student" table="t_student">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<!--
使用多对一标签映射 一对多双向,下列的column值必需与多的一端的key字段值一样。
-->
<many-to-one name="classes" column="classesid"/>
</class>
</hibernate-mapping>

session = HibernateUtils.getSession();
tx = session.beginTransaction(); Classes classes = new Classes();
classes.setName("wjt168");
session.save(classes); Student student1 = new Student();
student1.setName("10");
student1.setClasses(classes);
session.save(student1); Student student2 = new Student();
student2.setName("祖儿");
student2.setClasses(classes);
session.save(student2); //提交事务
tx.commit();

注意:一对多,从多的一端保存数据比从一对一端保存数据要快,因为从一对一端保存数据时,会多更新多的一端的一个外键(是指定一的一端的。)

如果在一对多的映射关系中采用一的一端来维护关系的话会存在以下两个缺点:①如果多的一端那个外键设置为非空时,则多的一端就存不进数据;②会发出多于的Update语句,这样会影响效率。所以常用对于一对多的映射关系我们在多对一端维护关系,并让多对一端维护关系失效(见下面属性)。

<hibernate-mapping>
<class name="h.one.bean.Classes" table="t_classes">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/> <!--
<set>标签 映射一对多(映射set集合),name="属性集合名称"
然后在用<key>标签,在多的一端加入一个外键(column属性指定列名称)指向一的一端
再采用<one-to-many>标签说明一对多,还指定<set>标签中name="students"这个集合中的类型
要使用完整的类路径(例如:class="com.wjt276.hibernate.Student")
inverse="false":一的一端维护关系失效(反转) :false:可以从一的一端维护关系(默认);true:从一的一端维护关系失效,这样如果在一的一端维护关系则不会发出Update语句。
-->
<set name="students" inverse="true">
<key column="classesid"/>
<one-to-many class="h.one.bean.Student"/>
</set>
</class>
</hibernate-mapping>

关于inverse属性:

inverse主要用在一对多和多对多双向关联上,inverse可以被设置到集合标签<set>上,默认inverse为false,所以我们可以从一对一端和多对一端维护关联关系,如果设置inverse为true,则我们只能从多的一端维护关联关系。

注意:inverse属性,只影响数据的存储,也就是持久化

Inverse和cascade区别:

Inverse是关联关系的控制方向

Casecade操作上的连锁反应

一对多双向关联映射总结:

在一对一端的集合上使用<key>,在对方表中加入一个外键指向一对一端

在多对一端采用<many-to-one>

注意:<key>标签指定的外键字段必须和<many-to-one>指定的外键字段一致,否则引用字段的错误

如果在一对一端维护一对多的关系,hibernate会发出多余的update语句,所以我们一般在多对一端来维护关系。

多对多关联映射 单向(many-to-many)

Ø 一般的设计中,多对多关联映射,需要一个中间表

Ø Hibernate会自动生成中间表

Ø Hibernate使用many-to-many标签来表示多对多的关联

Ø 多对多的关联映射,在实体类中,跟一对多一样,也是用集合来表示的。

实例场景:

用户与他的角色(一个用户拥有多个角色,一个角色还可以属于多个用户)

对象模型:

hibernate--关联映射(一对多)

多对多关联映射 单向数据存储:

@Test
public void testManyToMany(){
session = HibernateUtil.getSession();
tx = session.beginTransaction(); Role r1 = new Role();
r1.setName("数据录入人员");
session.save(r1); Role r2 = new Role();
r2.setName("商务主管");
session.save(r2); Role r3 = new Role();
r3.setName("大区经理");
session.save(r3); Users u1 = new Users();
u1.setName("10");
Set<Role> u1Roles = new HashSet<Role>();
u1Roles.add(r1);
u1Roles.add(r2);
u1.setRoles(u1Roles); Users u2 = new Users();
u2.setName("祖儿");
Set<Role> u2Roles = new HashSet<Role>();
u2Roles.add(r2);
u2Roles.add(r3);
u2.setRoles(u2Roles); Users u3 = new Users();
u3.setName("成龙");
Set<Role> u3Roles = new HashSet<Role>();
u3Roles.add(r1);
u3Roles.add(r2);
u3Roles.add(r3);
u3.setRoles(u3Roles); session.save(u1);
session.save(u2);
session.save(u3); tx.commit();
}

多对多关联映射 单向数据加载:

多对多关联映射 双向(many-to-many)

<hibernate-mapping>
<class name="h.one.bean.Role" table="t_role">
<id name="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<!— order-by 属性是第三方表哪个字段进行排序-->
<set name="users" table="t_user_role" order-by="userid">
<key column="roleid"/>
<many-to-many class="h.one.bean.Users" column="userid"/>
</set>
</class>
</hibernate-mapping>

注:数据的存储与单向一样。但一般维护这个多对多关系,只需要使用一方,而使另一方维护关系失效。

多对多关联映射 双向 数据加载

session = HibernateUtil.getSession();
tx = session.beginTransaction(); Role role = (Role)session.load(Role.class, 1);
System.out.println("role.name=" + role.getName());
for (Iterator<UserS> iter = role.getUsers().iterator();iter.hasNext();){
User user = iter.next();
System.out.println("user.name=" + user.getName());
}
//提交事务
tx.commit();

总结:

Ø table属性值必须和单向关联中的table属性值一致

Ø <key>中column属性值要与单向关联中的<many-to-many>标签中的column属性值一致

Ø 在<many-to-many>中的column属性值要与单向关联中<key>标签的column属性值一致。