`
hn_liuyi
  • 浏览: 32442 次
  • 性别: Icon_minigender_1
  • 来自: 天津
社区版块
存档分类
最新评论

在 Hibernate 中实现复杂的数据映射(转载)

    博客分类:
  • J2EE
阅读更多
级别: 初级

陈亚强 (cyqcims@mail.tsinghua.edu.cn), 高级软件工程师

2003 年 10 月 13 日

    在前一篇文章《使用Hibernate来操作持久对象》中,介绍了Hibernate的基本概念,然后用实例演示了怎么在Web应用中使用 Hibernate来封装持久数据对象。然而在现实的项目中,我们往往需要操作多个数据表,并且多个表之间往往存在复杂的关系,在本文,将介绍怎么在 Hibernate中描述多个表的映射关系,并且演示怎么操作关系复杂的持久对象。

阅读本文前您需要以下的知识和工具:

    * Tomcat 5.09,可以从 www.apache.org下载;
    * Hibernate2.0 相关运行环境,可以从 http://hibernate.bluemars.net/下载;
    * 至少安装了一个数据库服务器并且有相关的JDBC驱动程序。

本文的参考资料见 参考资料

本文的全部代码在这里 下载

案例介绍

在第一篇文章中,我们对一个表进行了简单的封装。在这篇文章中,我们讨论更加复杂的情况。

在这个例子中,将考虑到表之间的一对一、一对多、多对多的情况。如图1所示。

图1 实体之间的映射关系
图1 实体之间的映射关系

在上面的数据模型图中,Student是所有表的核心,它和Classes表是一对多的关系,和Course表是多对多的关系(通过Student_Course_Link表来链接),和Address表是一对一的关系。

通过分析,我们可以把上面的数据模型转换成如下的Java持久对象,如图2所示。

图2 持久对象之间的关系
图2 持久对象之间的关系

可以看出,数据模型图和Java持久对象的类图有非常大的相似性,但是不完全相同。比如Classes表和Student表是一对多的关系;在类图中,两者仍然是一对多的关系,但是在Classes类中添加了一个student属性,属性的类型是java.util.Set,它表示Classes对象中包含的所有Student对象。




回页首


创建Hibernate持久对象

已经对数据模型经过了分析,现在就可以创建持久对象了。持久对象之间的关系由图2所示的类图指定。

我们首先来看Student类,它是这个关系映射的核心,代码如例程1所示。

例程1 Student持久对象(Student.java)

package com.hellking.study.hibernate;
import java.util.Set;
/**
*在hibernate中代表了Students表的类。
*/
public class Student
{
   /**属性,和students表中的字段对应**/
   private String id;
   private String name;
   /**和其它类之间的映射关系**/
   private Set courses;
   private Classes classes;
   private Address address;
  
   /**属性的访问方法,必须是公共的方法**/
   public void setId(String string) {
id = string;
}

public String getId() {
return id;
}

public void setName(String name)
{
this.name=name;
}
public String getName()
{
return this.name;
}

/**操作和其它对象之间的关系**/
public void setCourses(Set co)
{
this.courses=co;
}
public Set getCourses()
{
return this.courses;
}
public void setAddress(Address ad)
{
this.address=address;
}
public Address getAddress()
{
return this.address;
}
public void setClasses(Classes c)
{
this.classes=c;
}
public Classes getClasses()
{
return this.classes;
}
}


在Student类中,由于 Students表和Classes的表是多对一的关系,故它包含了一个类型为Classes的classes属性,它的实际意义是一个学生可以有一个班级;Students表和Address的表是一对一的关系,同样也包含了一个类型为Address的address属性,它的实际意义是一个学生有一个地址;Students表和Course是多对多的关系,故它包含了一个类型为java.util.Set的course属性,它的实际意义是一个学生可以学习多门课程,同样,某个课程可以由多个学生来选修。

Classes对象和Student对象是一对多的关系。Classes对象包含一个类型为java.util.Set的students属性,它的代码如例程2所示。

例程2 Classes持久对象(Classes.java)

package com.hellking.study.hibernate;
import java.util.Set;
/**
*在hibernate中代表了Classes表的类。
*/
public class Classes
{
   /**属性,和classes表的字段一致**/
   private String id; 
   private String name;
   /**和其它类之间的映射关系**/
   private Set students;
  
   /**属性的访问方法,必须是公共的方法**/
   public void setId(String string) {
id = string;
}

public String getId() {
return id;
}

public void setName(String name)
{
this.name=name;
}
public String getName()
{
return this.name;
}

/**操作和其它对象之间的关系**/
public void setStudents(Set stud)
{
this.students=stud;
}
public Set getStudents()
{
return this.students;
}
}


Course持久对象在前一篇文章已经介绍,在这里就不再列举。Address持久对象比较简单,除了表字段定义的属性外,没有引入其它的属性,请参考本文的代码。





回页首


描述对象之间的关系

现在我们已经编写好了持久对象,下面的任务就是描述它们之间的关系。首先我们看Student持久对象的描述,如例程3所示。

例程3 Student持久对象的描述(Student.hbm.xml)

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
    <class
        name="com.hellking.study.hibernate.Student"
        table="Students"
        dynamic-update="false"
    >
   <!-- 描述ID字段-->
        <id
            name="id"
            column="StudentId"
            type="string"
            unsaved-value="any"
        >
            <generator class="assigned"/>
        </id>
     <!-- 属性-->
         <property
            name="name"
            type="string"
            update="true"
            insert="true"
            column="Name"
        />
<!-- 描述Student和Course多对多的关系-->
         <set
            name="courses"
            table="Student_Course_Link"
            lazy="false"
            inverse="false"
            cascade="all"
            sort="unsorted"
        >
              <key
                  column="StudentId"
              />
              <many-to-many
                  class="com.hellking.study.hibernate.Course"
                  column="CourseId"
                  outer-join="auto"
              />
        </set>
<!-- 描述Student和Classes之间多对一的关系-->
          <many-to-one
            name="classes"
            class="com.hellking.study.hibernate.Classes"
            cascade="none"
            outer-join="auto"
            update="true"
            insert="true"
            column="ClassesId"
        />
       
<!-- 描述Student和Address之间一对一的关系-->       
<one-to-one
            name="address"
            class="com.hellking.study.hibernate.Address"
            cascade="none"
            outer-join="auto"
            constrained="false"
        />      
    </class>
</hibernate-mapping>


在Student.hbm.xml描述符中,共描述了三种关系。第一种是Student和Address之间一对一的关系,它是最简单的关系,使用:

<one-to-one name="" class="">


来描述,这里的name表示的是Student对象中名称为address的属性;class表示的是address属性的类型:com.hellking.study.hibernate.Address。

接下来看Student和Classes之间多对一的关系,使用:

<many-to-one   name="classes" class="com.hellking.study.hibernate.Classes" column="ClassesId" …   />


来描述。同样,name表示的是 Student对象中名称为classes的属性;class表示的是classes属性的类型,column表示Student表引用Classes表使用的外部键名称。对应的,在Classes类中也引用了Student类,它使用了以下的描述符来描述这个关系:

        <set
            name="students"
            table="Students"
            lazy="false"
            inverse="false"
            cascade="all"
            sort="unsorted"
        >
              <key
                  column="ClassesId"
              />
              <one-to-many
                  class="com.hellking.study.hibernate.Student"
              />
        </set>
       


在描述Student和Course之间多对多关系时,使用了以下的方法:

        <set
            name="courses"
            table="Student_Course_Link"
            lazy="false"
            inverse="false"
            cascade="all"
            sort="unsorted"
        >
              <key
                  column="StudentId"
              />
              <many-to-many
                  class="com.hellking.study.hibernate.Course"
                  column="CourseId"
                  outer-join="auto"
              />
        </set>
       


在映射多对多关系时,需要另外使用一个链接表,这个表的名字由table属性指定,链接表包含了两个字段:CourseId和StudentId。以下的描述:

<key column="StudentId">


指定了Student对象在Student_Course_Link链接表中的外部键。对应的,在Course持久对象使用了以下的描述符来描述这个关系:

        <set
            name="students"
            table="Student_Course_Link"
            lazy="false"
            inverse="false"
            cascade="all"
            sort="unsorted"
        >
              <key
                  column="CourseId"
              />
              <many-to-many
                  class="com.hellking.study.hibernate.Student"
                  column="StudentId"
                  outer-join="auto"
              />
        </set>
       


由于其它持久对象的描述基本一样,在这里就不一一列举了,请参考本文的源代码。最后别忘了在hibernate.cfg.xml里增加这几个对象的描述。

        <!-- Mapping files -->
        <mapping resource="Address.hbm.xml"/>
        <mapping resource="Student.hbm.xml"/>
        <mapping resource="Classes.hbm.xml"/>
        <mapping resource="Course.hbm.xml"/
       






回页首


使用映射关系

下面我们开发一个简单的实例来测试这个映射。持久对象使用最频繁的操作是增加数据、查询数据、删除数据、更新数据。对于更新数据的操作的情况,多个表的操作和单个表没有两样,在这里不举例了。

添加数据到数据库

我们在这里测试前三种操作,首先来看添加数据到数据库的情况,如例程4所示。

例程4 测试持久对象之间的映射关系之添加数据(MapTestBean.java部分代码)

    /**
*在数据库中添加数据
*/
public void addData(String studentId,String classesId,String coursesId)
        throws HibernateException {
        try
        {
          /**
           *以下的代码添加了一个Student,同时为Student指定了
           *Address、Courses和Classses。
           */
          beginTransaction(); 
          //创建一个Student对象 。      
          Student student = new Student();
          student.setName("hellking2");
          student.setId(studentId);
         
          //创建一个Address对象。
          Address addr=new Address();
          addr.setCity("beijing");
          addr.setState("bj");
          addr.setStreet("tsinghua");
          addr.setZip("100083");
          addr.setId(student.getId());       
//设置Student和address的关系。
          student.setAddress(addr);      
        
         Set set=new HashSet();
         set.add(student);
         //创建一个course对象。
         Course course=new  Course  ();
         course.setId(coursesId);
         course.setName("computer_jsp");
         //设置course和student对象之间的关系。
         course.setStudents(set);
      
         //创建一个classes对象。
         Classes cl=new Classes();
         cl.setId(classesId);
         cl.setName("engine power");
         //设置某个classes对象包含的students对象。
         cl.setStudents(set);
        //由于是双向的关系,student对象也需要设置一次。
         student.setClasses(cl);       
        
         //保存创建的对象到session中。
         session.save(cl);
         session.save(course);
         session.save(student);
         session.save(addr);
         //提交事务,使更改生效。
         endTransaction(true);
       }
       catch(HibernateException e)
       {        
           System.out.println("在添加数据时出错!");
           e.printStackTrace();
          throw e;
       }
    }
   


在例程4中,添加数据到数据库之前,首先设置持久对象的各个属性,如:

student.setName("hellking2");


这种设置属性的方式和普通的类没有什么区别,设置完所有的属性后,就设置持久对象之间的关系,如:

student.setAddress(addr);


如果存在对象之间的多重关系,那么可能需要把对象保存在Set集合中,然后再进行设置,如:

Set set=new HashSet();
set.add(student);
course.setStudents(set);


当设置完所有的属性和对象关系之后,就可以调用:

session.save(persistentObject);


方法把持久对象保存到Hibernate会话中。最后,调用endTransaction来提交事务,并且关闭Hibernate会话。

数据查询

在复杂的实体对象映射中,往往查询也比较复杂。作为演示,我们在这里也提供了几个查询方法,如例程5所示。

例程5 测试持久对象之间的映射关系之查询数据(MapTestBean.java部分代码)

    /**
     *获得某个给定studentid的Student的地址信息
     */
    public Address getAddress(String id) throws HibernateException
    {   
        beginTransaction();   
    Student st=(Student)session.load(Student.class,id);   
    Address addr=(Address)session.load(Address.class,st.getId());
    endTransaction(false);
return addr;
  
    }
   
    /**
     *获得某个给定studentid的Student的所有课程
     */
    public Set getCourses(String id)throws HibernateException
    {
    beginTransaction();   
    Student st=(Student)session.load(Student.class,id);
     endTransaction(false);   
    return st.getCourses();
    }
   
    /**
     *测试获得某个给定studentid的Student所属的Classes
     */
    public Classes getClasses(String id)throws HibernateException
    {   
    beginTransaction();   
    Student st=(Student)session.load(Student.class,id);
    System.out.println(st.getClasses().getId()); 
    endTransaction(false);
    return st.getClasses();
    }
   


这里提供了三种查询方法,分别是:

    * 查询给定id的Student的Address信息;
    * 查询给定id的Student的所有Courses信息;
    * 查询给定id的Student所属的Classes信息。

在查询时,首先使用beginTransaction()方法创建一个Hibernate会话对象,并且开始一个新Hibernate事务;然后通过session.load()方法获得给定ID的Student对象,如:

Student st=(Student)session.load(Student.class,id);


最后调用student.getXXX()方法返回指定的对象。

删除数据

在表的关系比较复杂时,要删除数据,往往存在级联删除的情况,由于级联删除的情况比较复杂,在这里就不举例了。假设我们要删除和某个给定id的student对象的所有相关的记录,就可以使用例程6所示的方法。

例程6 测试持久对象之间的映射关系之删除数据(MapTestBean.java部分代码)

    /**
     *删除和某个学生相关的所有信息
     *(这里只是测试,我们暂且不说这种操作的意义何在)。
     */
    public void delteStudent(String id)throws HibernateException
    {
    beginTransaction();   
    Student st=(Student)session.load(Student.class,id);    
    Address addr=(Address)session.load(Address.class,st.getId());
    //删除address信息。
    session.delete(addr);
    //删除classes信息。
    session.delete(st.getClasses());
    /**
    *逐个删除course。
    */
        for(Iterator it=st.getCourses().iterator();it.hasNext();)
        {
        Course c=(Course)it.next();
        session.delete(c);
        }
        //最后,删除student对象。
    session.delete(st);
    endTransaction(true);
    }
   


同样,在执行删除前,首先使用beginTransaction()方法创建一个新Hibernate会话和一个新Hibernate事务,然后把要删除的对象Load进来,接下来调用session.delete()方法来删除指定对象。

如果要删除的是集合中的对象,那么可以通过一个迭代来逐个删除,如例程6中删除courses的方法。
[color=blue][/color]
分享到:
评论

相关推荐

    hibernate官方入门教程 (转载)

    1. **对象关系映射(ORM)**: ORM是将数据库中的表映射为Java对象的技术,Hibernate通过XML或注解方式实现这种映射,使得开发者可以使用面向对象的方式来处理数据。 2. **配置文件**: Hibernate的配置文件...

    jdbc与hibernate的优缺点比较(转载的精髓).pdf

    JDBC(Java Database Connectivity)是Java中用于与数据库交互的一组接口和类,而Hibernate是一个对象关系映射(ORM)框架,它将Java对象与数据库表之间的映射过程自动化。 1. **Hibernate的优点**: - **对象化...

    Hibernate缓存机制,转载

    **标题:“Hibernate缓存机制,转载”** **描述:**这篇博客主要探讨了Hibernate框架中的缓存机制,包括一级缓存和二级缓存的概念、工作原理以及如何在实际开发中应用。 **标签:“源码 工具”** **知识点详解:*...

    jsr168 portlet(struts2+spring2.5+hibernate3.3)(转载)

    它将Java对象映射到关系数据库表,提供了查询语言HQL(Hibernate Query Language)和 Criteria API,降低了数据访问的复杂性。在JSR168portlet中,Hibernate可能是用来处理portlet的数据持久化的工具。 【lib】目录...

    数据模型设计心得(转载)

    **源码**标签可能意味着文章中会提及如何将数据模型转化为实际的编程代码,比如SQL语句或者ORM框架(如Hibernate、MyBatis)的映射配置。源码层面的数据模型设计需要考虑性能、可维护性和扩展性。 **工具**标签可能...

    Servlet和Struts2的交互(转载)

    至于标签,Struts2提供了丰富的标签库,使得开发者可以更方便地在JSP页面中进行数据绑定和控制流操作,如`s:property`用于显示Action属性的值,`s:if`和`s:choose`进行条件判断等,这大大减少了纯Java代码的使用,...

    MyEclipse6Java开发中文教程

    该教程聚焦于最新的开源架构——Struts 2、Spring 2和Hibernate 3,这三大框架是企业级Java应用开发中的常用组件,它们分别负责MVC模式的实现、依赖注入以及对象关系映射。 Struts 2是Apache软件基金会的一个项目,...

    Java 最常见 200+ 面试题全解析:面试必备.pdf

    12. Hibernate:作为Java对象关系映射工具,Hibernate简化了数据持久化的复杂性,主要探讨其核心概念和使用方法。 13. MyBatis:讨论MyBatis框架的特点,以及如何进行SQL语句映射和结果集处理。 14. RabbitMQ:是...

    JAVA文章精选542个(txt) 免费分享

    这篇文章可能探讨了在Java中如何设计和实现数据持久化层,特别是如何将数据库中的记录映射到Java对象(ORM,对象关系映射)。可能涉及JPA(Java Persistence API)或Hibernate等框架,讲解如何创建实体类,配置映射...

    springmybatis

    查询出列表,也就是返回list, 在我们这个例子中也就是 List&lt;User&gt; , 这种方式返回数据,需要在User.xml 里面配置返回的类型 resultMap, 注意不是 resultType, 而这个resultMap 所对应的应该是我们自己配置的 ...

    本项目是基于SpringBoot的线上宠物物资购买系统,为个人毕业设计,未经允许禁止转载.zip

    6. **数据库连接与ORM框架**:SpringBoot集成了多种数据库连接池,如HikariCP,以及对象关系映射(ORM)框架如Hibernate或MyBatis,用于处理数据库操作。 7. **安全控制**:SpringBoot提供了Spring Security模块,...

    j2ee在线购物网实例源码

    通过分析和研究这个源码,开发者可以深入理解如何在实际项目中运用J2EE技术栈,包括Servlet、JSP、JDBC、EJB、Spring、Hibernate等,以及如何设计和实现一个功能完善的电子商务系统。 【标签】"j2ee在线购物网实例...

Global site tag (gtag.js) - Google Analytics