利用关联关系操作对象:
数据对象之间的关联关系有一对一,一对多及多对多三种。在数据库操作中,数据对象之间的关联关系使用JDBC处理很困难。例如,当删除一个班级的信息时,还要删除该班级的所有学生的基本信息。如果直接使用JDBC执行这种级联操作,会非常繁锁。Hibernate通过把实体对象之间的关联关系及级联关系在映射文件中声明,比较简单地解决了这类级联操作问题。
一对一关联关系的使用:
一对一关联关系在实际生活中是比较觉的,例如学生与学生证的关系,通过学生证可以找到学生。一对一关联关系在Hibernate中的实现有两种方式,分别是主键关联和外键关联。
主键关联:
主键关联的重点是,关联两个实体共享一个主键值。例如student与card是一对一关系,它们在数据库中对应的表分别是t_student和t_card。它们共用一个主键值ID,这个主键可由t_student或t_card表生成。问题是如何让另一张表引用已经生成的主键值呢?例如,t_student表未老先衰了ID的值,t_card表如何引用它?这需要在Hibernate的映射文件中使用主键的foreign生成机制!
为了表示Student与Card之间的一对一的关联关系,我们需要在它们各自的映射文件 中都要使用<one-to-one>标记!
一对一关系我在前面已经写过例子程序了,在这里仅给出两个映射文件。如下:
学生PO映射信息:
<?
xml version="1.0" encoding="GBK"
?>
<!
DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>
<
hibernate-mapping
>
<
class
name
="hibernate.PO.TStudent"
table
="t_student"
lazy
="true"
>
<!--
把类与表关联起来
-->
<
id
name
="id"
type
="java.lang.Integer"
>
<
column
name
="id"
/>
<
generator
class
="increment"
/>
</
id
>
<
property
name
="userName"
type
="java.lang.String"
>
<
column
name
="userName"
length
="20"
/>
</
property
>
<
property
name
="cardId"
type
="java.lang.String"
>
<
column
name
="card_id"
length
="20"
/>
</
property
>
<
property
name
="sex"
type
="java.lang.String"
>
<
column
name
="sex"
length
="2"
/>
</
property
>
<
property
name
="age"
type
="java.lang.Integer"
>
<
column
name
="age"
/>
</
property
>
<
one-to-one
name
="card"
class
="hibernate.PO.TCard"
fetch
="join"
cascade
="all"
/>
</
class
>
</
hibernate-mapping
>
<!--
class元素的lazy属性设定为true,表示延迟加载,如果lazy设为false,则
表示立即加载。以下对这二点进行说明。
立即加载:表示Hibernate在从数据库中取得数据组装好一个对象(如学生1)后,
会立即再从数据库取得数据组装此对象所关联的对象(如学生证1)。
延迟加载:表示Hibernate在从数据库中取得数据组装好一个对象(如学生1)后,
不会立即再从数据库中取得数据组装此对象所关联的对象(如学生1),
而是等到需要时,才会从数据库取得数据组装此关联对象。
<one-to-one>元素的cascade属性表明操作是否从父对象级联到被关联的对象, 它
的取得可以是以下几种:
none:在保存,删除或修改当前对象时,不对其附属对象(关联对象)进行级联
操作。它是默认值。
save-update:在保存,更新当前对象时,级联保存,更新附属对象(临时对象,
游离对象)。
delete:在删除当前对象时,级联删除附属对象。
all:所有情况下均进行级联操作,即包含save-update和delete操作。
delete-orphan:删除和当前对象解除关系的附属对象。
<one-to-one>元素的fetch属性的可选值是join和select,默认是select。
当fetch属性设定为join时,表示连接抓取(Join fetching):Hibernate通过
在Select语句中使用outer join(外连接)来获得对象的关联实例或者关联集合。
当fetch属性设定为select时,表示查询抓取(Select fetching):需要另外发
送一条Select语句抓取当前对象的关联实体或集合。
-->
学生证PO映射信息:
外键关联
:
外键关联的要点是:两个实体各自有不同的主键,但其中一个实体有一个外键引用另一个实体的主键。例如,假设,Student和Card是外键关联的一对一关系们在数据库中相应的表分别如下:t_student表有一个主键ID,t_card表有一个主键ID和一个外键student_id,此外键对应t_student表的主键ID,那么student的映射信息如上面一样不做改动,而Card的映射文件要做相应的改动。如下:
<
hibernate-mapping
>
<
class
name
="hibernate.PO.TCard"
table
="t_card"
lazy
="true"
>
<
id
name
="id"
type
="java.lang.Integer"
>
<
column
name
="id"
/>
<
generator
class
="increment"
/>
<!--
不再是foreign了
-->
</
id
>
<
property
name
="name"
type
="java.lang.String"
>
<
column
name
="name"
length
="20"
/>
</
property
>
<
many-to-one
name
="student"
column
="student_id"
class
="hibernate.PO.TStudent"
unique
="true"
/>
<!--
惟一的多对一,实际上变成一对一关系了
-->
<!--
unique设为true表示使用DDL为外键字段生成一个惟一约束。
以外键关联对象的一对一关系,其本质上已经变成了一对多的双向关联,
应直接按照一对多和多对一的要求编写它们的映射文件。当unique为
true时实际上变成了一对一的关系。
-->
</
class
>
</
hibernate-mapping
>
一对多关联关系的使用
一对多关系很觉,例如班级与学生的关系就是典型的一对多的关系。在实际编写程序时,一对多关系有两种实现方式:单向关联和双向关联。单向的一对多关系只需要在一方进行映射配置,而双向的一对多需要在关联的双方进行映射配置。下面以Group(班级)和Student(学生)为例讲解如何配置一对多的关系。
单向关联
:
单向的一对多关系只需要在一方进行映射配置,所以我们只配置Group的映射文件:
<
hibernate-mapping
>
<
class
name
="hibernate.PO.Group"
table
="t_group"
lazy
="true"
>
<
id
name
="id"
type
="java.lang.Integer"
>
<
column
name
="id"
/>
<
generator
class
="increment"
/>
</
id
>
<!--
insert属性表示被映射的字段是否出现在SQL的INSERT语句中
-->
<
property
name
="name"
type
="java.lang.String"
update
="true"
insert
="true"
>
<
column
name
="name"
length
="20"
/>
</
property
>
<!--
set元素描述的字段对应的类型为java.util.Set类型。
inverse用于表示双向关联中的被动一端。inverse的值
为false的一方负责维护关联关系。
sort排序关系,其可选值为:unsorted(不排序)。
natural(自然排序)。
comparatorClass(由某个实现了java.util.comparator接口的类型指定排序算法。)
<key>子元素的column属性指定关联表(t_student表)的外键。
-->
<
set
name
="students"
table
="t_student"
lazy
="true"
inverse
="false"
cascade
="all"
sort
="unsorted"
>
<
key
column
="ID"
/>
<
one-to-many
class
="hibernate.PO.TStudent"
/>
</
set
>
</
class
>
</
hibernate-mapping
>
双向关联
:
如果要设置一对多双向关联关系,那么还需要在“多”方的映射文件中使用<many-to-one>标记。例如,在Group与Student一对多的双向关联中,除了Group的映射文件外还需要在Student的映射文件中加入如下代码:
<
many-to-one
name
="group"
class
="Group"
cascade
="none"
outer-join
="auto"
update
="true"
insert
="true"
column
="ID"
/>
inert和update设定是否对column属性指定的关联字段进行insert和update操作。
此外将Group.hbm.xml中<set>元素的inverse设置为true.
多对多关联关系的使用
Student(学生)和Course(课程)的关系就是多对多关系。在映射多对多关系时需要另外使用一个连接表(如Student_Course)。Student_Course表包含二个字段:courseID和studentID。此处它们的映射文件中使用<many-to-many>标记,在Student的映射文件中加入以下描述信息:
<
set
name
="courses"
table
="student_course"
lazy
="false"
inverse
="false"
cascade
="save-update"
>
<
key
column
="studentID"
/>
<
many-to-many
class
="Course"
column
="CourseID"
/>
</
set
>
相应的Course的映射文件中加入以下:
<
set
name
="students"
table
="student_course"
lazy
="false"
inverse
="true"
cascade
="save-update"
>
<
key
column
="CourseID"
/>
<
many-to-many
class
="Student"
column
="StudentID"
/>
</
set
>
添加关联关系
:
首先编写一个程序来看看一个名为Bill的学生选择了什么课程:
现在Bill还想chemistry课程,这对于程序员来说只是为Bill添加一个到chemistry的关联,也就是说在student_course表中新增加一条记录,而T_student和T_Course表都不用变更。
//
获取代表Bill的Student对象
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
Student stu = (Student)session.createQuery("from Student s where s.name='Bill'").uniqueResult();
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
Course course = (Course)session.createQuery("from Course c where c.name='chemistry'").uniqueResult();
//
设置stu与course的关联关系
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
stu.getCourses().add(course);
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
course.getStudents().add(stu);
删除关联关系
:
删除关联关系比较简单,直接调用对象集合的remove()方法删除不要的对象就可。例如:要从学生Bill的选课清单中删除politics和chemistry两门课,程序代码如下:
//
获取代表Bill的Student对象
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
Student stu = (Student)session.createQuery("from Student s where s.name='Bill'").uniqueResult();
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
Course course1 = (Course)session.createQuery("from Course c where c.name='politics'").uniqueResult();
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
Course course2 = (Course)session.createQuery("from Course c where c.name='chemistry'").uniqueResult();
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
stu.getCourse().remove(course1);
//
删除politics课程
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
stu.getCourse().remove(course2);
//
删除chemistry课程
运行以上程序将从student_course表中删除这两条记录,但T_student和T_course表没有任何变化.
在数据库领域中,数据表和数据表之间关系一般可以分为如下几种:
单对单:比如公民和身份证的关系,一个人只有一张身份证,同时每张身份证也仅仅对应一个人!
单对多:比如单个客户和订单之间的关系,每个客户可以同时下多张订单!
多对多:比如学生管理系统中,学生与课程,教师与学生之间的关系!
上面是简单的说了下数据库表与表之间的关系,那么我们现在要说的是Hibernate,来看看Hibernate中如何使用表与表的关系,首先来看看我数据库中二个示例表user和card分别表示用户和身份证!二个表中分别有二字段,创建表的DDL如下:
create
table `study`.`card`(
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
`cardid`
int
default
''
not
null,
--
主键
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
`cardnum`
int,
--
身份证号
primary
key (`cardid`)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
);
create
unique
index `
PRIMARY`
on `study`.`card`(`cardid`);
create
table `study`.`
user`(
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
`userid`
int
not
null auto_increment,
--
主键,自动递增
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
`username`
varchar(
20),
--
用户名
primary
key (`userid`)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
);
create
unique
index `
PRIMARY`
on `study`.`
user`(`userid`);
create
table teachers
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
(
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
teaID
bigint auto_increment
primary
key,
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
teaName
varchar(
20)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
);
create
table students
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
(
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
stuID
bigint
primary
key auto_increment,
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
sName
varchar(
20)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
);
--
关联表 学生与教师
create
table student_teacher_table
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
(
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
teaID
int,
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
stuID
int
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
);
Step2:新建一个HibernateManyToMany项目 ,并添加hibernate。
Step3:我们将前面创建的teachers表和students表导向到Beans中,注意这里的关系表 student_teacher_table并不导出!因为我们数据做持久,持久的是对象,而不是关系, student_teacher_table表存放的是关系,所以我们不需要将 其导出,那么使用呢?接着看吧!
Step4:现在我们开始类对象关系的描述,我们先打开生成的Student.java 在里面添加Set 教师集合,然后在Teachers.java中同样添加Set学生集合!
Step5:我们描述了类与类之间的关系后,剩下的就是让Hibernate知道它们之间的关系,分别修改学生和教师类映射文件!
student.hbm.xml内容如下:
<?
xml version="1.0" encoding="GBK"
?>
<!
DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>
<!--
Autho:fengyan
date: 2006-12-30 17:04
-->
<
hibernate-mapping
>
<
class
name
="hibernate.beans.Students"
table
="students"
catalog
="study"
>
<
id
name
="stuId"
type
="java.lang.Long"
>
<
column
name
="stuID"
/>
<
generator
class
="native"
/>
</
id
>
<
property
name
="sname"
type
="java.lang.String"
>
<
column
name
="sName"
length
="20"
/>
</
property
>
<
set
name
="teachers"
table
="student_teacher_table"
cascade
="save-update"
inverse
="false"
>
<
key
column
="stuID"
></
key
>
<!--
它的控制主要是通过stuid来选择,就是我们从student_teacher_table表中我们只
要select * from student_teacher_table where stuID='该学生ID',这样我
们就可以获取它的教师的ID了
-->
<
many-to-many
class
="hibernate.beans.Teachers"
column
="teaID"
></
many-to-many
>
<!--
我们从student_teacher_table表中根据stuID拿到了与该stuID关联的teaID,
然后 select * from teacher where teaID='前一步拿到的teaID'
-->
</
set
>
</
class
>
</
hibernate-mapping
>
再看teacher.hbm.xml文件内容:
<?
xml version="1.0" encoding="GBK"
?>
<!
DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>
<!--
Authod:fengyan
date: 2006-12-30 17:10
-->
<
hibernate-mapping
>
<
class
name
="hibernate.beans.Teachers"
table
="teachers"
catalog
="study"
>
<
id
name
="teaId"
type
="java.lang.Long"
>
<
column
name
="teaID"
/>
<
generator
class
="native"
/>
</
id
>
<
property
name
="teaName"
type
="java.lang.String"
>
<
column
name
="teaName"
length
="20"
/>
</
property
>
<!--
前面在student中已经级联了,所以这里我没有再级联,简单的演示!
-->
<
set
name
="students"
table
="student_teacher_table"
inverse
="true"
>
<
key
column
="teaID"
></
key
>
<
many-to-many
class
="hibernate.beans.Students"
column
="stuID"
></
many-to-many
>
</
set
>
</
class
>
</
hibernate-mapping
>
然后我们现在建立一个StudentDAO.java用来封装操作:
package hibernateDAOS;
/**
* Authod:fengyan
* date: 2006-12-30 17:15
*/
import hibernate.HibernateSessionFactory;
import hibernate.beans.Students;
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
import org.hibernate.Session;
import org.hibernate.Transaction;
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
public class StudentsDAO {
public void save(Students student)
{
Session session = HibernateSessionFactory.getSession();
Transaction tx = session.beginTransaction();
try
{
session.save(student);
tx.commit();
}
catch(Exception e)
{
System.out.println("stuDAO has errors:"+e);
tx.rollback();
}
finally
{
session.close();
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
}
Step6:建立一个Servlet,MyServlet.java;
package hibernate.servlet;
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
import hibernate.beans.Students;
import hibernate.beans.Teachers;
import hibernateDAOS.StudentsDAO;
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
import java.io.IOException;
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
public class MyServlet extends HttpServlet {
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//产生学生
Students s1 = new Students("学生1");
Students s2 = new Students("学生2");
//产生教师
Teachers t1 = new Teachers("教师1");
Teachers t2 = new Teachers("教师2");
Teachers t3 = new Teachers("教师3");
Teachers t4 = new Teachers("教师4");
//学生关联教师(学生选择教师)
s1.getTeachers().add(t1);
s1.getTeachers().add(t2);
//学生 s1 选择 t1 及 t2 二名教师
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
s2.getTeachers().add(t1);
s2.getTeachers().add(t3);
//学生 s2 选择 t1 及 t3 二名教师
//我们保存学生 s1及 s2
StudentsDAO sd = new StudentsDAO();
sd.save(s1);
sd.save(s2);
}
}
Step7:最后来个测试index.jsp页面
<
a
href
="servlet/MyServlet"
>add
</
a
>
运行 结果:
![](https://i-blog.csdnimg.cn/blog_migrate/6e2b004535096a89427edfbb5939a4f3.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/bca66d038c78f47bcb74d78f62c5370b.jpeg)
由图片可知,
第一步:首先将 “学生1”插入students表中;
第二步:将"学生1"关联的“教师1”插入到teachers表中;
第三步:将“学生1”关联的“教师2”插入到teachers表中;
第四步:将“学生1”与“教师1”的关联信息(1,1)插入到student_teacher_table表中;
第五步:将“学生1”与“教师2”的关联信息(1,2)插入到student_teacher_table表中;
第六步:将“学生2”插入students表中;
第七步:将“教师3”插入teachers表中;
第八步:更新?更新了“学生2”关联~这步有点不解
第九步:将“学生2”与“教师1”的关联信息(2,1)插入到student_teacher_table表中;
第十步:将“学生2”与“教师3”的关联信息(2,3)插入student_teacher_table表中;
转载于http://www.cnblogs.com/eflylab/archive/2006/12/30/608163.html