4.3 Hibernate关系映射

    Hibernate关系映射的主要任务是实现数据库关系表与持久化类之间的映射java

1.一对一关联

    有两种实现方式:共享主键方式使两个数据表的主键使用相同的值,经过主键造成一对一映射关系)和惟一外键方式一个表的外键与另外一个表的惟一主键对应造成一对一映射关系)。sql

1)共享主键方式

    在注册某个论坛会员的时候,每每不但要填写登陆帐号和密码,还要填写其余的详细信息,这两部分信息一般会放在不一样的表中,如表4.一、表4.2所示。数据库

字 段 名 称session

数 据 类 型app

    ide

    测试

允 许 为 空this

    spa

ID.net

int(4)



ID

USERNAME

varchar(20)




登陆帐号

PASSWORD

varchar(20)




登陆密码

表4.1  登陆表Login 

字 段 名 称

数 据 类 型

    

    

允 许 为 空

    

ID

int(4)

1


ID

TRUENAME

varchar(8)



真实姓名

EMAIL

varchar(50)



电子邮件

表4.2  详细信息表Detail 

        登陆表和详细信息表属于典型的一对一关联关系,可按共享主键方式进行。步骤以下:

(1) 建立Java项目,命名为“Hibernate_mapping”。 

(2)添加Hibernate开发能力,步骤同4.2.1节第4步。HibernateSessionFactory类一样位于org.util包下。(注意先要配置数据库链接) 

(3)编写生成数据库表对应的Java类对象和映射文件。(注意放置的位置:选对项目名)   

 a)Login表对应的POJO类Login.java:

package org.model;
public class Login implements java.io.Serializable{
            private int id;                                               // ID号
            private String username;                              // 登陆帐号
            private String password;                              // 密码
            private Detail detail;                                 // 详细信息
            // 省略上述各属性的getter和setter方法
}


b)Detail表对应的Detail.java:

package org.model;
public class Detail implements java.io.Serializable{
           private int id;                                          // ID号
           private String trueName;                         // 真实姓名
           private String email;                             // 电子邮件
           private Login login;                             // 登陆信息
           // 省略上述各属性的getter和setter方法
}


 c)Login表与Login类的ORM映射文件Login.hbm.xml。

<?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>
  <!-- name指定POJO类,table指定对应数据库的表 -->
  <class name="org.model.Login" table="login">
    <!-- name指定主键,type指定主键类型 -->
    <id name="id" type="java.lang.Integer">
      <column length="4" name="ID"/>
      <!-- 采用foreign标志生成器,直接采用外键的属性值,达到共享主键的目的-->
      <generator class="foreign">
        <param name="property">detail</param>
      </generator>
    </id>
    <!-- POJO属性及表中字段的对应 -->
    <property name="username" type="java.lang.String">
      <column name="USERNAME"/>
    </property>
    <property name="password" type="java.lang.String">
      <column name="PASSWORD"/>
    </property>
    <!-- name表示属性名字,class表示被关联的类的名字,constrained="true"代表当前的主键上存在一个外键约束-->
    <one-to-one class="org.model.Detail" constrained="true" name="detail"/>
  </class>
</hibernate-mapping>


d)Detail表与Detail类的ORM映射文件Detail.hbm.xml:

<?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>
  <!-- name指定POJO类,table指定对应数据库的表 -->
  <class name="org.model.Detail" table="detail">
    <!-- name指定主键,type指定主键类型 -->
    <id name="id" type="java.lang.Integer">
      <column length="4" name="ID"/>
      <generator class="identity"/>
    </id>
    <!-- POJO属性及表中字段的对应 -->
    <property name="trueName" type="java.lang.String">
      <column name="TRUENAME"/>
    </property>
    <property name="email" type="java.lang.String">
      <column name="EMAIL"/>
    </property>
    <!-- name表示属性名字,class表示被关联的类的名字,cascade="all"代表主控类的全部操做,对关联类也执行一样操做,lazy="false"表示此关联为当即加载-->
    <one-to-one cascade="all" class="org.model.Login" lazy="false" name="login"/>
  </class>
</hibernate-mapping>


(4)在hibernate.cfg.xml文件中加入配置映射文件的语句。

<mapping resource="org/model/Detail.hbm.xml"/>
<mapping resource="org/model/Login.hbm.xml"/>

5)建立测试类。    在src文件夹下建立包test,在该包下创建测试类,命名为“Test.java”。其代码。

package test;
 
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.model.*;
import org.util.HibernateSessionFactory;
import java.sql.*;
 
public class Test {
    public static void main(String[] args) {
        // 调用HibernateSessionFactory的getSession方法建立Session对象
        Session session = HibernateSessionFactory.getSession();
        // 建立事务对象
        Transaction ts = session.beginTransaction();
        Detail detail = new Detail();
        Login login = new Login();
        login.setUsername("yanhong");
        login.setPassword("123");
        detail.setTrueName("严红");
        detail.setEmail("yanhong@126.com");
        // 相互设置关联
        login.setDetail(detail);
        detail.setLogin(login);
        // 这样完成后就能够经过Session对象调用session.save(detail)来持久化该对象
        session.save(detail);
        ts.commit();
        HibernateSessionFactory.closeSession();
    }
}


(6) 运行程序,测试结果。    由于该程序为Java Application,因此能够直接运行。在彻底没有操做数据库的状况下,程序就完成了对数据的插入。插入数据后,Login表和Detail表的内容如图4.十二、图4.13所示。

图4.12  Login表

图4.13  Detail表 

2)惟一外键方式

    惟一外键的状况不少,例如,每一个人对应一个房间。其实在不少状况下,能够是几我的住在同一个房间里面,就是多对一的关系。可是若是把这个多变成惟一,也就是说让一我的住一个房间,就变成了一对一的关系了,这就是前面说的一对一的关系实际上是多对一关联关系的一种特殊状况。对应的Person表和Room表如表4.三、表4.4所示。

字 段 名 称

数 据 类 型

    

    

允 许 为 空

    

Id

int

1


ID

name

varchar(20)




姓名

room_id

int



房间号

表4.3  Person表

字 段 名 称

数 据 类 型

    

    

允 许 为 空

    

id

int(4)

1


ID

address

varchar(100)




地址

表4.4  Room表  

步骤以下:

(1)在项目Hibernate_mapping的org.model包下编写生成数据库表对应的Java类对象和映射文件。    

Person表对应的POJO类Person.java:

package org.model;
public class Person implements java.io.Serializable {
    private Integer id;
    private String name;
    private Room room;
    // 省略上述各属性的getter和setter方法
}


Room表对应的POJO类Room.java:

package org.model;
public class Room implements java.io.Serializable{
    private int id; 
    private String address;
    private Person person;
    // 省略上述各属性的getter和setter方法
}


Person表与Person类的ORM映射文件Person.hbm.xml

<?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> 
    <class name="org.model.Person" table="Person"> 
        <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="room" column="room_id" class="org.model.Room"
            cascade="all" unique="true" />
       <!--     name="room"                    属性名称
                column="room_id"               充当外键的字段名
                class="org.model.Room"         被关联的类的名称
                cascade="all"                  主控类全部操做,对关联类也执行一样操做
                unique="true"                  惟一性约束,实现一对一  --> 
    </class> 
</hibernate-mapping>

去掉roomId的配置

<property name="roomId" type="java.lang.Integer">
    <column name="room_id" />
</property>

Room表与Room类的ORM映射文件Room.hbm.xml

<?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>
    <class name="org.model.Room" table="Room"> 
        <id name="id" column="id"> 
            <generator class="native"/> 
        </id>                                                        
        <property name="address" column="address" type="java.lang.String"/>             
        <one-to-one name="person"                  
              class="org.model.Person"                
              property-ref="room"/>                 
        <!--   name="person"                   属性名
               class="org.model.Person"        被关联的类的名称
               property-ref="room"             指定关联类的属性名      -->   
    </class>
</hibernate-mapping>

(2)在hibernate.cfg.xml文件中加入以下的配置映射文件的语句。

<mapping resource="org/model/Person.hbm.xml"/>
<mapping resource="org/model/Room.hbm.xml"/>


(3)编写测试代码。    

在src文件夹下的包test的Test类中加入以下代码:

Person person=new Person();
person.setName("liumin");
Room room=new Room();
room.setAddress("NJ-S1-328");
person.setRoom(room);
session.save(person);


4)运行程序,测试结果。    

由于该程序为Java Application,因此能够直接运行。在彻底没有操做数据库的状况下,程序就完成了对数据的插入。插入数据后,Person表和Room表的内容如图4.1四、图4.15所示。

图4.14  Person表

 

图4.15  Room表    

2.多对一单向关联

    只要把上例中的一对一的惟一外键关联实例稍微修改就能够变成多对一。步骤以下: 

(1)在项目Hibernate_mapping的org.model包下编写生成数据库表对应的Java类对象和映射文件

其对应表不变,Person表对应的类也不变,对应的Person.hbm.xml文件修改以下(只是去掉 <many-to-one/>中的unique="true"):

<?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> 
    <class name="org.model.Person" table="Person"> 
        <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="room"                  
                     column="room_id"                 
                     class="org.model.Room"         
                     cascade="all"/>        
    </class> 
</hibernate-mapping>


Room表不变,对应的POJO类以下:(去掉private Person person及其方法;)

package org.model;
public class Room implements java.io.Serializable{
    private int id; 
    private String address;
    // 省略上述各属性的getter和setter方法
}

Room表与Room类的ORM映射文件Room.hbm.xml以下(去掉<one-to-one/>配置):

<?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> 
    <class name="org.model.Room" table="room"> 
        <id name="id" column="id"> 
            <generator class="native"/> 
        </id> 
        <property name="address" 
                  column="address" 
                  type="java.lang.String"/>
    </class> 
</hibernate-mapping>

由于是单向多对一,因此无需在“一”的一边指定“多”的一边。(比如一班学生能够记住一个老师,这个老师记不住每一个学生)

(2)编写测试代码。    

在src文件夹下的包test的Test类中加入以下代码(跟上例同样,能够修改一下数据):

Person person=new Person();
person.setName("liuyanmin");
Room room=new Room();
room.setAddress("NJ-S1-328");
person.setRoom(room);
session.save(person);

(3) 运行程序,测试结果。    

由于该程序为Java Application,因此能够直接运行。在彻底没有操做数据库的状况下,程序就完成了对数据的插入。插入数据后,Person表和Room表的内容如图4.1六、图4.17所示。

图4.16  Person表 

图4.17  Room表 

3.一对多双向关联

    多对一单向关联,“一”的一方也知道“多”的一方,就变成了一对多(或多对一)双向关联下面经过修改4.3.2节的例子来完成双向多对一的实现。步骤以下: 

(1) 在项目Hibernate_mapping的org.model包下编写生成数据库表对应的Java类对象和映射文件。    

Person表对应的POJO及其映射文件不用改变,如今来修改Room表对应的POJO类及其映射文件。对应的POJO类Room.java。

package org.model;
import java.util.HashSet;
import java.util.Set;
public class Room {
    private int id;
    private String address;
    private Set person=new HashSet();  //集合,存放多个Person对象
        public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id=id;
    }
    public String getAddress() {
        return address;
    }
 
    public void setAddress(String address) {
        this.address=address;
    }
    public Set getPerson() {
        return person;
    }
    public void setPerson(Set person) {
        this.person=person;
    }
}


Room表与Room类的ORM映射文件Room.hbm.xml

<?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> 
    <class name="org.model.Room" table="room"> 
        <id name="id" column="id"> 
            <generator class="native"/> 
        </id> 
        <property name="address" column="address" type="java.lang.String"/>
        <set name="person"            
                 inverse="false"            
                 cascade="all">             
             <key column ="room_id" />                      
             <one-to-many class="org.model.Person" />       
        <!--      name="person"             此属性为Set集合类型,由name指定属性名字
                 inverse="false"           表示关联关系的维护工做由谁来负责,默认false,表示由主控方负责;true表示由被控方负责。因为该例是双向操做,故须要设为false,也可不写
                 cascade="all"             级联程度
             key column ="room_id"                      充当外键的字段名
             one-to-many class="org.model.Person"       被关联的类名字 -->
      </set>
    </class> 
</hibernate-mapping>


该配置文件中cascade配置的是级联程度,它有如下几种取值:

  • all:表示全部操做句在关联层级上进行连锁操做。

  • save-update:表示只有save和update操做进行连锁操做。

  • delete:表示只有delete操做进行连锁操做。

  • all-delete-orphan:在删除当前持久化对象时,它至关于delete;在保存或更新当前持久化对象时,它至关于save-update。另外它还能够删除与当前持久化对象断开关联关系的其余持久化对象。

(2)编写测试代码。在src文件夹下的包test的Test类中加入以下代码:

Person person1=new Person();
Person person2=new Person();
Room room=new Room();
room.setAddress("NJ-S1-328");
person1.setName("李方方");
person2.setName("王艳");
person1.setRoom(room);
person2.setRoom(room);
//这样完成后就能够经过Session对象调用session.save(person1)和session.save(person)会自动保存room
session.save(person1);
session.save(person2);


(3)运行程序,测试结果。    由于该程序为Java Application,因此能够直接运行。在彻底没有操做数据库的状况下,程序就完成了对数据的插入。插入数据后,Person表和Room表的内容如图4.1八、图4.19所示。


图4.18  Person表

图4.19  Room表 

因为是双向的,固然也能够从Room的一方来保存Person,在Test.java中加入以下代码:

Person person1=new Person();
Person person2=new Person();
Room room=new Room();
room.setAddress("NJ-S1-328");
person1.setName("李方方");
person2.setName("王艳");
Set persons=new HashSet();
persons.add(person1);
persons.add(person2);
room.setPerson(persons);
//这样完成后,就能够经过Session对象调用session.save(room)会自动保存person1和person2
session.save(room);

运行程序,插入数据后,Person表和Room表的内容如图4.20、图4.21所示。

图4.20  Person表

 

图4.21  Room表 

4.多对多关联

1)多对多单向关联

    学生和课程就是多对多的关系,一个学生能够选择多门课程,而一门课程又能够被多个学生选择。多对多关系在关系数据库中不能直接实现,还必须依赖一张链接表。如表4.六、表4.7和表4.8所示。

字 段 名 称

数 据 类 型

主    键

自    增

允 许 为 空

描    述

ID

int

增1


ID号

SNUMBER

varchar(10)




学号

SNAME

varchar(10)



姓名

SAGE

int



年龄

表4.6  学生表student 

字 段 名 称

数 据 类 型

主    键

自    增

允 许 为 空

描    述

ID

int

增1


ID号

CNUMBER

varchar(10)




课程号

CNAME

varchar(20)



课程名

表4.7  课程表course 

字 段 名 称

数 据 类 型

主    键

自    增

允 许 为 空

描    述

SID

int



学生ID号

CID

int



课程ID号

表4.8  链接表stu_cour     

因为是单向的也就是说从一方能够知道另外一方,反之不行。这里以从学生知道选择了哪些课程为例实现多对多单向关联。步骤以下:

(1)在项目Hibernate_mapping的org.model包下编写生成数据库表对应的Java类对象和映射文件。student表对应的POJO类以下:

package org.model;
import java.util.HashSet;
import java.util.Set;
 
public class Student implements java.io.Serializable{
private int id;
private String snumber;
private String sname;
private int sage;
private Set courses=new HashSet();
 //省略上述各属性的getter和setter方法
}


student表与Student类的ORM映射文件Student.hbm.xml

<?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>
<class name="org.model.Student" table="student">
        <id name="id" type="java.lang.Integer">
            <column name="ID" length="4" />
            <generator class="identity"/>
        </id>
        <property name="snumber" type="java.lang.String">
            <column name="SNUMBER"/>
        </property>
        <property name="sname" type="java.lang.String">
            <column name="SNAME" />
        </property>
        <property name="sage" type="java.lang.Integer">
            <column name="SAGE"></column>
        </property>
 
        <set name="courses"           // set标签表示此属性为Set集合类型,由name指定属性名称
                table="stu_cour"      // 链接表的名称
                lazy="true"           // 表示此关联为延迟加载,所谓延迟加载就是到了用的时候进行加载,避免大量暂时无用的关系对象
                cascade="all">        // 级联程度
            <key column="SID"></key>                          // 指定参照student表的外键名称
            <many-to-many class="org.model.Course"            // 被关联的类的名称
                          column="CID"/>                      // 指定参照course表的外键名称
        </set>
    </class>
</hibernate-mapping>

course表对应的POJO以下:

package org.model;
public class Course implements java.io.Serializable{
private int id;
           private String cnumber;
           private String cname;
           //省略上述各属性的getter和setter方法。
}

course表与Course类的ORM映射文件Course.hbm.xml

<?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>
    <class name="org.model.Course" table="course">
        <id name="id" type="java.lang.Integer">
            <column name="ID" length="4" />
            <generator class="identity"/>
        </id>
 
        <property name="cnumber" type="java.lang.String">
            <column name="CNUMBER"/>
        </property>
 
        <property name="cname" type="java.lang. String ">
            <column name="CNAME" />
        </property>
    </class>
</hibernate-mapping>


(2)在hibernate.cfg.xml文件中加入以下的配置映射文件的语句。

<mapping resource="org/model/Student.hbm.xml"/>
<mapping resource="org/model/Course.hbm.xml"/>


(3)编写测试代码。在src文件夹下的包test的Test类中加入以下代码。

Course cour1=new Course();
Course cour2=new Course();
Course cour3=new Course();
cour1.setCnumber("101");
cour1.setCname("计算机基础");
cour2.setCnumber("102");
cour2.setCname("数据库原理");
cour3.setCnumber("103");
cour3.setCname("计算机原理");
Set courses=new HashSet();
courses.add(cour1);
courses.add(cour2);
courses.add(cour3);
Student stu=new Student();
stu.setSnumber("081101");
stu.setSname("李方方");
stu.setSage(21);
stu.setCourses(courses);
session.save(stu);
//设置完成后就能够经过Session对象调用session.save(stu)完成持久化


(4) 运行程序,测试结果。    由于该程序为Java Application,因此能够直接运行。在彻底没有操做数据库的状况下,程序就完成了对数据的插入。插入数据后,student表、course表及链接表stu_cour表的内容如图4.2二、图4.2三、图4.24所示。

图4.22  student表

图4.23  course表

图4.24  stu_cour表 

2)多对多双向关联

    同时实现 两个互逆的多对多单项关联便可    首先将其Course表所对应的POJO对象修改为以下代码:

package org.model;
import java.util.HashSet;
import java.util.Set;
public class Course implements java.io.Serializable{
           private int id;
           private String cnumber;
           private String cname;
           private Set stus=new HashSet();
           //省略上述各属性的getter和setter方法
}

Course表与Course类的ORM映射文件Course.hbm.xml

<?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>
    <class name="org.model.Course" table="course">
        <id name="id" type="java.lang.Integer">
            <column name="ID" length="4" />
            <generator class="identity"/>
        </id>
        <property name="cnumber" type="java.lang.String">
            <column name="CNUMBER"/>
        </property>
        <property name="cname" type="java.lang.String">
            <column name="CNAME" />
        </property>
        <set name="stus"                // set标签表示此属性为Set集合类型,由name指定一个属性名称
                table="stu_cour"                       // 链接表的名称
                lazy="true"                            // 关联为延迟加载
                cascade="all">                         // 级联操做为全部
             <key column="CID"></key>        // 指定参照course表的外键名称
             <many-to-many class="org.model.Student"        // 被关联的类名
                column="SID"/>                        // 指定参照student表的外键名称
        </set>
    </class>
</hibernate-mapping>

实际用法与多对多单项关联相同,只是主控方不一样而已


附:目录《JavaEE基础实用教程》笔记说明

相关文章
相关标签/搜索