我的 O/R Mapping 之旅

    首先要感谢我所在的 Team 意识到了传统 JDBC 开发的种种不足,转而开始关注 O/R Mapping 领域的成果。说到 O/R Mapping ,我认为在对象数据库还没有真正成熟的时候它是一个不错的选择,看看 SUN JDO 2.0 规范吧,它的即将发布将会把这个领域推向更高的境界。再看看我们的开源社区呢,喔!发现了 Hibernate !从现在开始,我的焦点将放在 Hibernate 上面,一个优秀的 O/R Mapping 工具。

 

 

 

 

 

 

 

 

 

 

   

 

 

 

 

 

 

 

 

 

 

 

    在没有正式开始旅行之前,让我们区分几个名词。POJO:在 Hibernate 中代表包含 SeterGeter 这些最基本操作的值对象。而 BO:代表包含一些业务逻辑的值对象,它的作用域很大,也就是说 BO 在充当持久类的同时可以传到 UI 层。PO:代表持久对象,是纳入 Hibernate 管理框架中的,在一定程度上可以和值对象的概念互换,值对象经过 Hibernate 进行处理,就变成了 PO Hibernate 配置文件:hibernate.cfg.xml hibernate.properties,不过推荐使用 XML 格式。映射文件 *.hbm.xml:映射文件的作用是将 POJO 与关系型数据库数据相绑定,作为一个桥梁。另外,为数据库中的表进行手工编写映射文件可不是件好差事,幸好开源社区中也有一群同样想法的人,他们开发了 hibernateSynchronizer 映射工具,可到 http://www.binamics.com/hibernatesync/eclipse2.1/ 下载。

 

 

 

 

 

 

 

 

 

 

 

   

 

 

 

 

 

 

 

 

 

 

 

    好了,现在去 http://www.hibernate.org 下载 Hibernate 的开发包(目前的版本是 2.1.6)。接着打开 Eclipse 2.1,在更新管理器中安装 hibernateSynchronizer。据说 Eclipse 3.0 已内置映射工具,不过我没有试。安装完毕后,新建一个 Web 应用程序 HibernateTest,接着为这个应用程序添加 Hibernate 类库 hibernate-2.1.6/hibernate-2.1/hibernate2.jar 及其依赖类库、数据库连接包,强烈建议把 hibernate-2.1.6/hibernate-2.1/lib 下的所有类库全部加载,如下图:

 

      

 

    接着使用 hibernateSynchronizer 来生成 hibernate.cfg.xml 文件,新建——>其他——>Hibernate Configuration File,我使用的是 SQL Server 数据库,各项配置参数见下图:

 

 

 

 

 

 

 

 

 

 

 

   

 

     生成出来的 hibernate.cfg.xml 文件:

 

 

 

 

 

 

 

 

 

 

 

   

 

    在项目的 src 目录下新建四个包,分别是bobo.basebo.mappingcom.dao,具体什么作用,到时候他们都会一一呈献。在开始映射文件前还要做一件事,为应用程序 HibernateTest 配置 hibernateSynchronizer ,我更喜欢自己写 DAO ,所以没有配置 Data Access Objects,其他各项参数如下图:

 

 

 

      被映射的表 AutoInfo 结构如下,id为其主键:

 

 

 

 

 

 

 

 

 

 

 

 

                 

 

                 

 

    新建——>其他——>Hibernate Mapping File,配置好参数再“Refresh”后,选择要映射的表,注意千万不要在 800*600 下映射文件!否则有些按钮不会出现,各项配置参数见下图:

 

 

 

 

 

 

 

 

 

 

 

 

   

 

   

 

       终于、终于,映射文件 AutoInfo.hbm.xml 终于出来了!

 

 

 

 

 

 

 

 

 

 

 

 

    <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo.base">
 <class name="AutoInfo" table="AutoInfo">
  <property
   column="owner_dept"
   length="500"
   name="OwnerDept"
   not-null="false"
   type="string"
   />
  <property
   column="license_plate"
   length="50"
   name="LicensePlate"
   not-null="false"
   type="string"
   />
  <property
   column="owner"
   length="50"
   name="Owner"
   not-null="false"
   type="string"
   />
  <property
   column="owner_adderss"
   length="1000"
   name="OwnerAdderss"
   not-null="false"
   type="string"
   />
  <property
   column="id"
   length="18"
   name="Id"
   not-null="true"
   type="integer"
   />
 </class>
</hibernate-mapping>

    

 

    <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo.base">
 <class name="AutoInfo" table="AutoInfo">
  <property
   column="owner_dept"
   length="500"
   name="OwnerDept"
   not-null="false"
   type="string"
   />
  <property
   column="license_plate"
   length="50"
   name="LicensePlate"
   not-null="false"
   type="string"
   />
  <property
   column="owner"
   length="50"
   name="Owner"
   not-null="false"
   type="string"
   />
  <property
   column="owner_adderss"
   length="1000"
   name="OwnerAdderss"
   not-null="false"
   type="string"
   />
  <property
   column="id"
   length="18"
   name="Id"
   not-null="true"
   type="integer"
   />
 </class>
</hibernate-mapping>

    

 

    需要修改一下 AutoInfo.hbm.xml 文件为其定义主键,把:

 

 

 

 

 

 

 

 

 

 

 

 

    <property
     column="id"
     length="18"
     name="Id"
     not-null="true"
     type="integer"
    />
    

 

    <property
     column="id"
     length="18"
     name="Id"
     not-null="true"
     type="integer"
    />
    

 

替换为:

 

 

 

 

 

 

 

 

 

 

 

 

    <id name="Id" column="id" type="integer">
       <generator class="native"/>
    </id>
    

 

    <id name="Id" column="id" type="integer">
       <generator class="native"/>
    </id>
    

 

    再接再厉,为映射文件生成 POJO。修改 hibernate.cfg.xml 文件在 </session-factory> 标签的上面加上刚才映射的文件 <mapping resource="bo/mapping/AutoInfo.hbm.xml" />。接着在“包资源管理器”中点击 AutoInfo.hbm.xml 右键,Hibernate Synchronizer——>Synchronize Files。再看看 src 中的包:

 

 

 

 

 

 

 

 

 

 

 

 

               

 

               

 

在上一部分我只讲到如何通过数据库中的表生成映射文件和 POJO。在这一部分中,我将讲解映射文件。

 

    先看看这张表:

                   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

在现实的车辆管理系统中,绝对不会把车辆信息和拥有人信息放在一张表中。应该是“一个拥有者”拥有一或多个“车辆”。来看看分解后的表,PEOPLE 表,设置 OWNER_ID 主键:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

              

 

              

 

    AUTO_INFO 表,设置 AUTO_ID 主键:

 

 

 

 

 

 

 

 

 

 

 

 

 

             

   
为两表配置主、外键关系,设置 PEOPLE 表为主表:

 

             

   
为两表配置主、外键关系,设置 PEOPLE 表为主表:

 

             

   
为两表配置主、外键关系,设置 PEOPLE 表为主表:

 

      

 

打开 Eclipse,为这两张表生成映射文件。

 

AutoInfo.hbm.xml

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo">
 <class name="AutoInfo" table="AUTO_INFO">
  <id
   column="AUTO_ID"
   name="Id"
   type="integer"
  >
   <generator class="vm" />
  </id>
  <property
   column="LICENSE_PLATE"
   length="20"
   name="LicensePlate"
   not-null="false"
   type="string"
   />
  <many-to-one
   class="People"
   name="OwnerNo"
   not-null="true"
  >
   <column name="OWNER_NO" />
  </many-to-one>
 </class>
</hibernate-mapping>

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo">
 <class name="AutoInfo" table="AUTO_INFO">
  <id
   column="AUTO_ID"
   name="Id"
   type="integer"
  >
   <generator class="vm" />
  </id>
  <property
   column="LICENSE_PLATE"
   length="20"
   name="LicensePlate"
   not-null="false"
   type="string"
   />
  <many-to-one
   class="People"
   name="OwnerNo"
   not-null="true"
  >
   <column name="OWNER_NO" />
  </many-to-one>
 </class>
</hibernate-mapping>

 

People.hbm.xml

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo">
 <class name="People" table="PEOPLE">
  <id
   column="OWNER_ID"
   name="Id"
   type="integer"
  >
   <generator class="vm" />
  </id>
  <property
   column="NAME"
   length="50"
   name="Name"
   not-null="false"
   type="string"
   />
  <property
   column="ADDRESS"
   length="1000"
   name="Address"
   not-null="false"
   type="string"
   />
  <set inverse="true" name="AutoInfoSet">
   <key column="OWNER_NO" />
   <one-to-many class="AutoInfo" />
  </set>
 </class>
</hibernate-mapping>

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo">
 <class name="People" table="PEOPLE">
  <id
   column="OWNER_ID"
   name="Id"
   type="integer"
  >
   <generator class="vm" />
  </id>
  <property
   column="NAME"
   length="50"
   name="Name"
   not-null="false"
   type="string"
   />
  <property
   column="ADDRESS"
   length="1000"
   name="Address"
   not-null="false"
   type="string"
   />
  <set inverse="true" name="AutoInfoSet">
   <key column="OWNER_NO" />
   <one-to-many class="AutoInfo" />
  </set>
 </class>
</hibernate-mapping>

 

 


AutoInfo.hbm.xml 进行详细分析:

 

DOCTYPE 定义了这个 XML 文件规范 DTD,可以通过后面的 URL 获取,或者在 Hibernate 的 CLASSPATH 中获取。

    hibernate-mapping package="bo" 定义了POJO所存在的包“bo”。

 

class name="AutoInfo" table="AUTO_INFO" 定义了 POJO 的名称“AutoInfo”和数据库中的表名“AUTO_INFO”。

 

id column="AUTO_ID" name="Id" type="integer" 定义了数据库表中的主键字段,为相应的 POJO 的每个实例包含唯一标识。column 为数据库表字段名,name 标识属性名字,type 说明在 POJO 中的类型。另外,还有一个重要标识:unsaved-value,将会在以后用到。

 

Generator 为每个 POJO 的实例提供唯一标识。一般情况,我们使用native。class 表示采用由生成器接口net.sf.hibernate.id.IdentifierGenerator 实现的某个实例,其中包括:

 

 

 

 

 

 

 

 

 

 

 

assigned

 

 

 

 

 

 

 

 

 

 

 

主键由外部程序负责生成,在 save() 之前指定一个。

 

hilo

 

 

 

 

 

 

 

 

 

 

 

通过hi/lo 算法实现的主键生成机制,需要额外的数据库表或字段提供高位值来源。

 

seqhilo

 

 

 

 

 

 

 

 

 

 

 

hilo 类似,通过hi/lo 算法实现的主键生成机制,需要数据库中的 Sequence,适用于支持 Sequence 的数据库,如Oracle

 

increment

 

 

 

 

 

 

 

 

 

 

 

主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加1作为主键。这种方式可能产生的问题是:不能在集群下使用。

 

identity

 

 

 

 

 

 

 

 

 

 

 

采用数据库提供的主键生成机制。如DB2SQL ServerMySQL 中的主键生成机制。

 

sequence

 

 

 

 

 

 

 

 

 

 

 

采用数据库提供的 sequence 机制生成主键。如 Oralce 中的Sequence

 

native

 

 

 

 

 

 

 

 

 

 

 

Hibernate 根据使用的数据库自行判断采用 identityhilosequence 其中一种作为主键生成方式。

 

uuid.hex

 

 

 

 

 

 

 

 

 

 

 

Hibernate 基于128 UUID 算法 生成16 进制数值(编码后以长度32 的字符串表示)作为主键。

 

uuid.string

 

 

 

 

 

 

 

 

 

 

 

uuid.hex 类似,只是生成的主键未进行编码(长度16),不能应用在 PostgreSQL 数据库中

 

foreign

 

 

 

 

 

 

 

 

 

 

 

使用另外一个相关联的对象的标识符作为主键。
    property column="LICENSE_PLATE" length="20" name="LicensePlate" not-null="false" type="string" 

    property column="LICENSE_PLATE" length="20" name="LicensePlate" not-null="false" type="string" 
    property column="LICENSE_PLATE" length="20" name="LicensePlate" not-null="false" type="string"

定义了 POJO 中的属性,分别表示数据库表中的字段、长度、POJO 属性名称以及类型、非空状态。

    many-to-one class="People" name="OwnerNo" not-null="true" column name="OWNER_NO"
定义了一个持久化对象与另一个持久化对象的关系,这种关联模型是多对一的关联。应用在车辆管理系统中,代表着多台车辆由一个拥有者拥有。分别表示关联类的名字(由反射机制得到类型)、属性名称、非空状态、字段名。

 

分析完 AutoInfo.hbm.xml 后,再来看看 People.hbm.xml。大部分内容都已经在前面出现过,不同的地方是:

 


<set inverse="true" name="AutoInfoSet">
  <key column="OWNER_NO" />
  <one-to-many class="AutoInfo" />
</set>

 


<set inverse="true" name="AutoInfoSet">
  <key column="OWNER_NO" />
  <one-to-many class="AutoInfo" />
</set>

 

在车辆管理系统中,代表着一个拥有者拥有多台车辆。以 java.util.Set 类型表示。 inverse 用于标识双向关联中的被动方一端。inverse=false 的一方(主控方)负责维护关联关系;在车辆管理系统中, AutoInfo 作为主控方,应该把它设为“true”。这就好比你(被动方 one)在某个聚会上散发了许多名片,但是有可能你不清楚接收者(主动方 many)的具体背景;这个不要紧,接收者在必要的时候会和你联系就是了(主动维护关系)。

 

另外在 set 节点的属性中还有一个重要标识级联(cascade)关系,指明哪些操作(insert、update、delete)会从主控方对象级联到关联的对象。

 

可选值:

 

 

 

 

 

 

 

 

 

 

 

all: save()saveOrUpdate()update()delete() 均进行级联操作。

 

 

 

 

 

 

 

 

 

 

 

none: 所有情况下均不进行级联操作。

 

 

 

 

 

 

 

 

 

 

 

save-update: 在执行 save()saveOrUpdate()update() 时进行级联操作。

 

 

 

 

 

 

 

 

 

 

 

delete: 只在执行 delete() 时进行级联操作。

 

级联(cascade)在 Hibernate 映射关系中是个非常重要的概念。它指的是当主控对象调用 save-update delete 方法时,是否同时对关联对象(被动方)进行 save-update delete 。在这个映射文件中,当拥有者(People)被更新或者删除时,其所关联的车辆(AutoInfo)可以被修改或删除,所以应该把级联关系设置为cascade=”all”

 

 

 

 

 

 

 

 

 


<set inverse="true" name="AutoInfoSet">
  <key column="OWNER_NO" />
  <one-to-many class="AutoInfo" />
</set>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo">
 <class name="People" table="PEOPLE">
  <id
   column="OWNER_ID"
   name="Id"
   type="integer"
  >
   <generator class="vm" />
  </id>
  <property
   column="NAME"
   length="50"
   name="Name"
   not-null="false"
   type="string"
   />
  <property
   column="ADDRESS"
   length="1000"
   name="Address"
   not-null="false"
   type="string"
   />
  <set inverse="true" name="AutoInfoSet">
   <key column="OWNER_NO" />
   <one-to-many class="AutoInfo" />
  </set>
 </class>
</hibernate-mapping>

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo">
 <class name="AutoInfo" table="AUTO_INFO">
  <id
   column="AUTO_ID"
   name="Id"
   type="integer"
  >
   <generator class="vm" />
  </id>
  <property
   column="LICENSE_PLATE"
   length="20"
   name="LicensePlate"
   not-null="false"
   type="string"
   />
  <many-to-one
   class="People"
   name="OwnerNo"
   not-null="true"
  >
   <column name="OWNER_NO" />
  </many-to-one>
 </class>
</hibernate-mapping>

 

             

   
为两表配置主、外键关系,设置 PEOPLE 表为主表:

 

 

 

              

 

 

 

 

 

 

 

 

 

               

 

 

 

    <id name="Id" column="id" type="integer">
       <generator class="native"/>
    </id>
    

 

 

 

    <property
     column="id"
     length="18"
     name="Id"
     not-null="true"
     type="integer"
    />
    

 

 

 

    <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo.base">
 <class name="AutoInfo" table="AutoInfo">
  <property
   column="owner_dept"
   length="500"
   name="OwnerDept"
   not-null="false"
   type="string"
   />
  <property
   column="license_plate"
   length="50"
   name="LicensePlate"
   not-null="false"
   type="string"
   />
  <property
   column="owner"
   length="50"
   name="Owner"
   not-null="false"
   type="string"
   />
  <property
   column="owner_adderss"
   length="1000"
   name="OwnerAdderss"
   not-null="false"
   type="string"
   />
  <property
   column="id"
   length="18"
   name="Id"
   not-null="true"
   type="integer"
   />
 </class>
</hibernate-mapping>

    

 

 

 

   

 

 

 

                 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

   

 

 

 

 

 

 

 

 

 

 

 

    在没有正式开始旅行之前,让我们区分几个名词。POJO:在 Hibernate 中代表包含 SeterGeter 这些最基本操作的值对象。而 BO:代表包含一些业务逻辑的值对象,它的作用域很大,也就是说 BO 在充当持久类的同时可以传到 UI 层。PO:代表持久对象,是纳入 Hibernate 管理框架中的,在一定程度上可以和值对象的概念互换,值对象经过 Hibernate 进行处理,就变成了 PO Hibernate 配置文件:hibernate.cfg.xml hibernate.properties,不过推荐使用 XML 格式。映射文件 *.hbm.xml:映射文件的作用是将 POJO 与关系型数据库数据相绑定,作为一个桥梁。另外,为数据库中的表进行手工编写映射文件可不是件好差事,幸好开源社区中也有一群同样想法的人,他们开发了 hibernateSynchronizer 映射工具,可到 http://www.binamics.com/hibernatesync/eclipse2.1/ 下载。

 

 

 

 

 

 

 

 

 

 

 

   

 

 

 

 

 

 

 

 

 

 

 

    好了,现在去 http://www.hibernate.org 下载 Hibernate 的开发包(目前的版本是 2.1.6)。接着打开 Eclipse 2.1,在更新管理器中安装 hibernateSynchronizer。据说 Eclipse 3.0 已内置映射工具,不过我没有试。安装完毕后,新建一个 Web 应用程序 HibernateTest,接着为这个应用程序添加 Hibernate 类库 hibernate-2.1.6/hibernate-2.1/hibernate2.jar 及其依赖类库、数据库连接包,强烈建议把 hibernate-2.1.6/hibernate-2.1/lib 下的所有类库全部加载,如下图:

 

      

 

    接着使用 hibernateSynchronizer 来生成 hibernate.cfg.xml 文件,新建——>其他——>Hibernate Configuration File,我使用的是 SQL Server 数据库,各项配置参数见下图:

 

 

 

 

 

 

 

 

 

 

 

   

 

     生成出来的 hibernate.cfg.xml 文件:

 

 

 

 

 

 

 

 

 

 

 

   

 

    在项目的 src 目录下新建四个包,分别是bobo.basebo.mappingcom.dao,具体什么作用,到时候他们都会一一呈献。在开始映射文件前还要做一件事,为应用程序 HibernateTest 配置 hibernateSynchronizer ,我更喜欢自己写 DAO ,所以没有配置 Data Access Objects,其他各项参数如下图:

 

 

 

      被映射的表 AutoInfo 结构如下,id为其主键:

 

 

 

 

 

 

 

 

 

 

 

 

                 

 

                 

 

    新建——>其他——>Hibernate Mapping File,配置好参数再“Refresh”后,选择要映射的表,注意千万不要在 800*600 下映射文件!否则有些按钮不会出现,各项配置参数见下图:

 

 

 

 

 

 

 

 

 

 

 

 

   

 

   

 

       终于、终于,映射文件 AutoInfo.hbm.xml 终于出来了!

 

 

 

 

 

 

 

 

 

 

 

 

    <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo.base">
 <class name="AutoInfo" table="AutoInfo">
  <property
   column="owner_dept"
   length="500"
   name="OwnerDept"
   not-null="false"
   type="string"
   />
  <property
   column="license_plate"
   length="50"
   name="LicensePlate"
   not-null="false"
   type="string"
   />
  <property
   column="owner"
   length="50"
   name="Owner"
   not-null="false"
   type="string"
   />
  <property
   column="owner_adderss"
   length="1000"
   name="OwnerAdderss"
   not-null="false"
   type="string"
   />
  <property
   column="id"
   length="18"
   name="Id"
   not-null="true"
   type="integer"
   />
 </class>
</hibernate-mapping>

    

 

    <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo.base">
 <class name="AutoInfo" table="AutoInfo">
  <property
   column="owner_dept"
   length="500"
   name="OwnerDept"
   not-null="false"
   type="string"
   />
  <property
   column="license_plate"
   length="50"
   name="LicensePlate"
   not-null="false"
   type="string"
   />
  <property
   column="owner"
   length="50"
   name="Owner"
   not-null="false"
   type="string"
   />
  <property
   column="owner_adderss"
   length="1000"
   name="OwnerAdderss"
   not-null="false"
   type="string"
   />
  <property
   column="id"
   length="18"
   name="Id"
   not-null="true"
   type="integer"
   />
 </class>
</hibernate-mapping>

    

 

    需要修改一下 AutoInfo.hbm.xml 文件为其定义主键,把:

 

 

 

 

 

 

 

 

 

 

 

 

    <property
     column="id"
     length="18"
     name="Id"
     not-null="true"
     type="integer"
    />
    

 

    <property
     column="id"
     length="18"
     name="Id"
     not-null="true"
     type="integer"
    />
    

 

替换为:

 

 

 

 

 

 

 

 

 

 

 

 

    <id name="Id" column="id" type="integer">
       <generator class="native"/>
    </id>
    

 

    <id name="Id" column="id" type="integer">
       <generator class="native"/>
    </id>
    

 

    再接再厉,为映射文件生成 POJO。修改 hibernate.cfg.xml 文件在 </session-factory> 标签的上面加上刚才映射的文件 <mapping resource="bo/mapping/AutoInfo.hbm.xml" />。接着在“包资源管理器”中点击 AutoInfo.hbm.xml 右键,Hibernate Synchronizer——>Synchronize Files。再看看 src 中的包:

 

 

 

 

 

 

 

 

 

 

 

 

               

 

               

 

在上一部分我只讲到如何通过数据库中的表生成映射文件和 POJO。在这一部分中,我将讲解映射文件。

 

    先看看这张表:

                   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

在现实的车辆管理系统中,绝对不会把车辆信息和拥有人信息放在一张表中。应该是“一个拥有者”拥有一或多个“车辆”。来看看分解后的表,PEOPLE 表,设置 OWNER_ID 主键:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

              

 

              

 

    AUTO_INFO 表,设置 AUTO_ID 主键:

 

 

 

 

 

 

 

 

 

 

 

 

 

             

   
为两表配置主、外键关系,设置 PEOPLE 表为主表:

 

             

   
为两表配置主、外键关系,设置 PEOPLE 表为主表:

 

             

   
为两表配置主、外键关系,设置 PEOPLE 表为主表:

 

      

 

打开 Eclipse,为这两张表生成映射文件。

 

AutoInfo.hbm.xml

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo">
 <class name="AutoInfo" table="AUTO_INFO">
  <id
   column="AUTO_ID"
   name="Id"
   type="integer"
  >
   <generator class="vm" />
  </id>
  <property
   column="LICENSE_PLATE"
   length="20"
   name="LicensePlate"
   not-null="false"
   type="string"
   />
  <many-to-one
   class="People"
   name="OwnerNo"
   not-null="true"
  >
   <column name="OWNER_NO" />
  </many-to-one>
 </class>
</hibernate-mapping>

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo">
 <class name="AutoInfo" table="AUTO_INFO">
  <id
   column="AUTO_ID"
   name="Id"
   type="integer"
  >
   <generator class="vm" />
  </id>
  <property
   column="LICENSE_PLATE"
   length="20"
   name="LicensePlate"
   not-null="false"
   type="string"
   />
  <many-to-one
   class="People"
   name="OwnerNo"
   not-null="true"
  >
   <column name="OWNER_NO" />
  </many-to-one>
 </class>
</hibernate-mapping>

 

People.hbm.xml

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo">
 <class name="People" table="PEOPLE">
  <id
   column="OWNER_ID"
   name="Id"
   type="integer"
  >
   <generator class="vm" />
  </id>
  <property
   column="NAME"
   length="50"
   name="Name"
   not-null="false"
   type="string"
   />
  <property
   column="ADDRESS"
   length="1000"
   name="Address"
   not-null="false"
   type="string"
   />
  <set inverse="true" name="AutoInfoSet">
   <key column="OWNER_NO" />
   <one-to-many class="AutoInfo" />
  </set>
 </class>
</hibernate-mapping>

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo">
 <class name="People" table="PEOPLE">
  <id
   column="OWNER_ID"
   name="Id"
   type="integer"
  >
   <generator class="vm" />
  </id>
  <property
   column="NAME"
   length="50"
   name="Name"
   not-null="false"
   type="string"
   />
  <property
   column="ADDRESS"
   length="1000"
   name="Address"
   not-null="false"
   type="string"
   />
  <set inverse="true" name="AutoInfoSet">
   <key column="OWNER_NO" />
   <one-to-many class="AutoInfo" />
  </set>
 </class>
</hibernate-mapping>

 

 


AutoInfo.hbm.xml 进行详细分析:

 

DOCTYPE 定义了这个 XML 文件规范 DTD,可以通过后面的 URL 获取,或者在 Hibernate 的 CLASSPATH 中获取。

    hibernate-mapping package="bo" 定义了POJO所存在的包“bo”。

 

class name="AutoInfo" table="AUTO_INFO" 定义了 POJO 的名称“AutoInfo”和数据库中的表名“AUTO_INFO”。

 

id column="AUTO_ID" name="Id" type="integer" 定义了数据库表中的主键字段,为相应的 POJO 的每个实例包含唯一标识。column 为数据库表字段名,name 标识属性名字,type 说明在 POJO 中的类型。另外,还有一个重要标识:unsaved-value,将会在以后用到。

 

Generator 为每个 POJO 的实例提供唯一标识。一般情况,我们使用native。class 表示采用由生成器接口net.sf.hibernate.id.IdentifierGenerator 实现的某个实例,其中包括:

 

 

 

 

 

 

 

 

 

 

 

assigned

 

 

 

 

 

 

 

 

 

 

 

主键由外部程序负责生成,在 save() 之前指定一个。

 

hilo

 

 

 

 

 

 

 

 

 

 

 

通过hi/lo 算法实现的主键生成机制,需要额外的数据库表或字段提供高位值来源。

 

seqhilo

 

 

 

 

 

 

 

 

 

 

 

hilo 类似,通过hi/lo 算法实现的主键生成机制,需要数据库中的 Sequence,适用于支持 Sequence 的数据库,如Oracle

 

increment

 

 

 

 

 

 

 

 

 

 

 

主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加1作为主键。这种方式可能产生的问题是:不能在集群下使用。

 

identity

 

 

 

 

 

 

 

 

 

 

 

采用数据库提供的主键生成机制。如DB2SQL ServerMySQL 中的主键生成机制。

 

sequence

 

 

 

 

 

 

 

 

 

 

 

采用数据库提供的 sequence 机制生成主键。如 Oralce 中的Sequence

 

native

 

 

 

 

 

 

 

 

 

 

 

Hibernate 根据使用的数据库自行判断采用 identityhilosequence 其中一种作为主键生成方式。

 

uuid.hex

 

 

 

 

 

 

 

 

 

 

 

Hibernate 基于128 UUID 算法 生成16 进制数值(编码后以长度32 的字符串表示)作为主键。

 

uuid.string

 

 

 

 

 

 

 

 

 

 

 

uuid.hex 类似,只是生成的主键未进行编码(长度16),不能应用在 PostgreSQL 数据库中

 

foreign

 

 

 

 

 

 

 

 

 

 

 

使用另外一个相关联的对象的标识符作为主键。
    property column="LICENSE_PLATE" length="20" name="LicensePlate" not-null="false" type="string" 

    property column="LICENSE_PLATE" length="20" name="LicensePlate" not-null="false" type="string" 
    property column="LICENSE_PLATE" length="20" name="LicensePlate" not-null="false" type="string"

定义了 POJO 中的属性,分别表示数据库表中的字段、长度、POJO 属性名称以及类型、非空状态。

    many-to-one class="People" name="OwnerNo" not-null="true" column name="OWNER_NO"
定义了一个持久化对象与另一个持久化对象的关系,这种关联模型是多对一的关联。应用在车辆管理系统中,代表着多台车辆由一个拥有者拥有。分别表示关联类的名字(由反射机制得到类型)、属性名称、非空状态、字段名。

 

分析完 AutoInfo.hbm.xml 后,再来看看 People.hbm.xml。大部分内容都已经在前面出现过,不同的地方是:

 


<set inverse="true" name="AutoInfoSet">
  <key column="OWNER_NO" />
  <one-to-many class="AutoInfo" />
</set>

 


<set inverse="true" name="AutoInfoSet">
  <key column="OWNER_NO" />
  <one-to-many class="AutoInfo" />
</set>

 

在车辆管理系统中,代表着一个拥有者拥有多台车辆。以 java.util.Set 类型表示。 inverse 用于标识双向关联中的被动方一端。inverse=false 的一方(主控方)负责维护关联关系;在车辆管理系统中, AutoInfo 作为主控方,应该把它设为“true”。这就好比你(被动方 one)在某个聚会上散发了许多名片,但是有可能你不清楚接收者(主动方 many)的具体背景;这个不要紧,接收者在必要的时候会和你联系就是了(主动维护关系)。

 

另外在 set 节点的属性中还有一个重要标识级联(cascade)关系,指明哪些操作(insert、update、delete)会从主控方对象级联到关联的对象。

 

可选值:

 

 

 

 

 

 

 

 

 

 

 

all: save()saveOrUpdate()update()delete() 均进行级联操作。

 

 

 

 

 

 

 

 

 

 

 

none: 所有情况下均不进行级联操作。

 

 

 

 

 

 

 

 

 

 

 

save-update: 在执行 save()saveOrUpdate()update() 时进行级联操作。

 

 

 

 

 

 

 

 

 

 

 

delete: 只在执行 delete() 时进行级联操作。

 

级联(cascade)在 Hibernate 映射关系中是个非常重要的概念。它指的是当主控对象调用 save-update delete 方法时,是否同时对关联对象(被动方)进行 save-update delete 。在这个映射文件中,当拥有者(People)被更新或者删除时,其所关联的车辆(AutoInfo)可以被修改或删除,所以应该把级联关系设置为cascade=”all”

 

 

 

 

 

 

 

 

 


<set inverse="true" name="AutoInfoSet">
  <key column="OWNER_NO" />
  <one-to-many class="AutoInfo" />
</set>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo">
 <class name="People" table="PEOPLE">
  <id
   column="OWNER_ID"
   name="Id"
   type="integer"
  >
   <generator class="vm" />
  </id>
  <property
   column="NAME"
   length="50"
   name="Name"
   not-null="false"
   type="string"
   />
  <property
   column="ADDRESS"
   length="1000"
   name="Address"
   not-null="false"
   type="string"
   />
  <set inverse="true" name="AutoInfoSet">
   <key column="OWNER_NO" />
   <one-to-many class="AutoInfo" />
  </set>
 </class>
</hibernate-mapping>

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo">
 <class name="AutoInfo" table="AUTO_INFO">
  <id
   column="AUTO_ID"
   name="Id"
   type="integer"
  >
   <generator class="vm" />
  </id>
  <property
   column="LICENSE_PLATE"
   length="20"
   name="LicensePlate"
   not-null="false"
   type="string"
   />
  <many-to-one
   class="People"
   name="OwnerNo"
   not-null="true"
  >
   <column name="OWNER_NO" />
  </many-to-one>
 </class>
</hibernate-mapping>

 

             

   
为两表配置主、外键关系,设置 PEOPLE 表为主表:

 

 

 

              

 

 

 

 

 

 

 

 

 

               

 

 

 

    <id name="Id" column="id" type="integer">
       <generator class="native"/>
    </id>
    

 

 

 

    <property
     column="id"
     length="18"
     name="Id"
     not-null="true"
     type="integer"
    />
    

 

 

 

    <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "
http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping package="bo.base">
 <class name="AutoInfo" table="AutoInfo">
  <property
   column="owner_dept"
   length="500"
   name="OwnerDept"
   not-null="false"
   type="string"
   />
  <property
   column="license_plate"
   length="50"
   name="LicensePlate"
   not-null="false"
   type="string"
   />
  <property
   column="owner"
   length="50"
   name="Owner"
   not-null="false"
   type="string"
   />
  <property
   column="owner_adderss"
   length="1000"
   name="OwnerAdderss"
   not-null="false"
   type="string"
   />
  <property
   column="id"
   length="18"
   name="Id"
   not-null="true"
   type="integer"
   />
 </class>
</hibernate-mapping>

    

 

 

 

   

 

 

 

                 

 

 

 

 

 

 

 

 

 

 

 

 

在本部分中,才真正开始对 AUTO_INFO PEOPLE 表进行操作。

 

    要让 Hibernate 跑起来,还要了解其中几个关键对象:

 

 

 

 

 

 

 

 

 

        net.sf.hibernate.cfg.Configuration 的实例负责管理 Hibernate 配置信息,比如数据库连接、数据库 dialect,还有最重要的映射文件初始化工作。

 

 

 

 

 

 

 

 

 

        程序为了得到 Session 实例,必须先要得到它的工厂 net.sf.hibernate.SessionFactory,SessionFactory 的实例 Configuration 构造。

 

 

 

 

 

 

 

 

 

    net.sf.hibernate.Session 是一切数据库操作的基础,和 JDBC 的 Connection 意义一样,Session 实例由 SessionFactory 获得

 

 

 

 

 

 

 

 

 

    net.sf.hibernate.Transaction 的实例由 Session 获得Hibernate 不具备事务管理能力,Hibernate 将其委托给底层的 JDBC 或者 JTA,以实现事务管理和调度功能。在本文中使用 JDBC 事务管理。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

把以上的知识串起来吧:

 

 

 

 

 

 

 

 

 

 

Configuration cfg = new Configuration().configure();

 

 

 

 

 

 

 

 

 

SessionFactory sessions = cfg.buildSessionFactory();

 

 

 

 

 

 

 

 

 

Session session = sessions.openSession();

 

 

 

 

 

 

 

 

 

Transaction tx = session.beginTransaction();

 

 

 

 

 

 

  

 

Configuration cfg = new Configuration().configure();

 

 

 

 

 

 

 

 

 

SessionFactory sessions = cfg.buildSessionFactory();

 

 

 

 

 

 

 

 

 

Session session = sessions.openSession();

 

 

 

 

 

 

 

 

 

Transaction tx = session.beginTransaction();

 

 

 

 

 

 

  

 

程序第一幕马上出场。

有位叫张三的人,他买了辆车。由于是第一次买车,进入车辆管理系统后要对AUTO_INFO PEOPLE 表都进行 insert 操作。

 

形成的一对多(one-to-many)关系保存如下:

 

package com.dao;

import java.util.*;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import bo.*;

public class Test {

 List list;
 AutoInfo ai=new AutoInfo();
    People people=new People();
 public void DoTest() {
  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   Transaction tx = session.beginTransaction();
   
   people.setAddress("中国");
   people.setName("张三");
   people.addToAutoInfoSet(ai);
   ai.setLicensePlate("A00001");
   ai.setOwnerNo(people);
   session.save(people);
   tx.commit();
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}

 

package com.dao;

import java.util.*;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import bo.*;

public class Test {

 List list;
 AutoInfo ai=new AutoInfo();
    People people=new People();
 public void DoTest() {
  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   Transaction tx = session.beginTransaction();
   
   people.setAddress("中国");
   people.setName("张三");
   people.addToAutoInfoSet(ai);
   ai.setLicensePlate("A00001");
   ai.setOwnerNo(people);
   session.save(people);
   tx.commit();
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}

 

分别设置好 AutoInfoPeople 对象属性后,调用 Session.save() 方法保存,然后事务提交,最后关闭 Session。好了,看看数据库吧,一切都已保存好了。

 

程序第二幕出场。

张三后来做生意,自己经营得很好,打算再买辆车跑运输。对于第二次买车,车辆管理系统的 PEOPLE 表原本已经记录了他的基本信息,遂不对 PEOPLE 表操作。只向 AUTO_INFO insert 一条车辆记录即可。

 

形成的一对多(one-to-many)关系保存如下:

 

package com.dao;

import java.util.*;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import bo.*;

public class Test {

 List list;
 AutoInfo ai = new AutoInfo();
 People people = new People();
 public void DoTest() {
  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   Transaction tx = session.beginTransaction();
   people =
    (People) session
     .find(
      "from People where OWNER_ID=1")
     .get(0);
   ai.setLicensePlate("A00002");
   ai.setOwnerNo(people);
   people.getAutoInfoSet().add(ai);
   session.save(people);
   tx.commit();
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}

 

package com.dao;

import java.util.*;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import bo.*;

public class Test {

 List list;
 AutoInfo ai = new AutoInfo();
 People people = new People();
 public void DoTest() {
  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   Transaction tx = session.beginTransaction();
   people =
    (People) session
     .find(
      "from People where OWNER_ID=1")
     .get(0);
   ai.setLicensePlate("A00002");
   ai.setOwnerNo(people);
   people.getAutoInfoSet().add(ai);
   session.save(people);
   tx.commit();
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}

 

session.find() 方法返回一个 List 接口的实例,里面封装着 PO,其中的 from People where OWNER_ID=1 就是大名鼎鼎的 HQL(Hibernate Query Language) 了,是不是很像 SQL 呢?PEOPLE 表和 AUTO_INFO 表存在一对多关系,也就需要 People 对象来持有多个 AutoInfo 对象(以 Set 接口的实例封装,参看People类源代码),再通过 people.getAutoInfoSet().add(ai) AUTO_INFO 表添加一条新纪录。好了,执行完以后,再检查数据库吧。

 

这段代码

 

 

 

 

 

 

 

 

 

people = (People) session.find("from People where OWNER_ID=1").get(0);

可以和

people =(People) session.load(People.class,new Integer(1));

互换。 在本例中 List 接口实例 size() 为 1,即其中只有一个 PO;而 session.load() 是根据持久对象和主键来返回相应 PO,也只是单个。所以这两种方式返回的都是相同 PO。采用哪种方式由你决定。

 

到这里,也许你会有这样的想法:“应该可以直接向 AUTO_INFO 表插入记录,不通过 People 对象中转,像写 SQL 一样 Easy。” 错了!以前直接写 SQL 是可以办到的,不过现在我们用的可是 Hibernate ,一切都要以对象行事,看见 ai.setOwnerNo(people) 了吗?传入参数是个 People 对象实例,不是简单的字段喔。

 

程序第三幕出场。

呵呵,这个第三幕是最简单的了,第一种一对多(one-to-many)的查询:

 

package com.dao;

import java.util.*;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import bo.*;

public class Test {

 List list;
 AutoInfo ai = new AutoInfo();
 People people = new People();
 public void DoTest() {
  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   List list =
    session.find(
     "select ai from AutoInfo as ai where ai.OwnerNo.Id=1");
   for (int i = 0; i < list.size(); i++) {
    ai=(AutoInfo)list.get(i);
    System.out.println(ai.getLicensePlate());
    people=ai.getOwnerNo();
    System.out.println(people.getName());
    System.out.println(people.getAddress());
   }
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}

  

 

package com.dao;

import java.util.*;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import bo.*;

public class Test {

 List list;
 AutoInfo ai = new AutoInfo();
 People people = new People();
 public void DoTest() {
  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   List list =
    session.find(
     "select ai from AutoInfo as ai where ai.OwnerNo.Id=1");
   for (int i = 0; i < list.size(); i++) {
    ai=(AutoInfo)list.get(i);
    System.out.println(ai.getLicensePlate());
    people=ai.getOwnerNo();
    System.out.println(people.getName());
    System.out.println(people.getAddress());
   }
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}

  

 

到了年底,查查张三一共有多少辆车。select ai from AutoInfo as ai where ai.OwnerNo.Id=1,这句 HQL 要找的是 AutoInfo 对象,而传统 SQL 这么写:“select p.name,p.address,ai.license_plate from people p,auto_info ai where p.owner_id=1”。由于 AUTO_INFO 表是“many”表,而 PEOPLE 表是“one”表,我的做法是以“many”表为基础返回它的多个 PO,其中再持有“one”表的信息。“ai.OwnerNo.Id=1”就是说通过主键参数为“1”的 PEOPLE 表记录来取出相应 AUTO_INFO 表记录。

 

对于一对多(one-to-many)关系,还有第二种查询方式:

 

package com.dao;

import java.util.*;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import bo.*;

public class Test {

 AutoInfo ai = new AutoInfo();
 People people = new People();
 public void DoTest() {
  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   people = (People) session.load(People.class, new Integer(1));
   Iterator iterator = people.getAutoInfoSet().iterator();
   System.out.println(people.getName());
   System.out.println(people.getAddress());
   while (iterator.hasNext()) {
    ai = (AutoInfo) iterator.next();
    System.out.println(ai.getLicensePlate());
   }
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}

  

 

package com.dao;

import java.util.*;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import bo.*;

public class Test {

 AutoInfo ai = new AutoInfo();
 People people = new People();
 public void DoTest() {
  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   people = (People) session.load(People.class, new Integer(1));
   Iterator iterator = people.getAutoInfoSet().iterator();
   System.out.println(people.getName());
   System.out.println(people.getAddress());
   while (iterator.hasNext()) {
    ai = (AutoInfo) iterator.next();
    System.out.println(ai.getLicensePlate());
   }
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}

  

 

    people = (People) session.load(People.class, new Integer(1)) 取出单个 People 对象,其中持有以 Set 封装的若干 AutoInfo 对象实例,Iterator iterator = people.getAutoInfoSet().iterator() 把 Set 立即转化为 Iterator ,最后用 while (iterator.hasNext()) 循环取出其中的车牌号码。

 

    第一种一对多(one-to-many)的查询实际上我把它转换成了多对一(many -to- one)的查询。这种转换后有两个缺点:一、由 HQL 转为 SQL 输出时,打印的 SQL 条数多于直接一对多(one-to-many)关系查询;二、其中的 People 对象存在着大量冗余(只需要一个实例,结果取出了 List.size() 个相同的实例)。我们知道,数据库的性能是有限的,构造对象的代价是高昂的,所以应尽量减少不必要的性能开销。

 

    虽然我个人不建议把一对多(one-to-many)查询转换成多对一(many -to- one)查询,但事实上有些开发团队却乐意采用,即使他们知道性能有略微的降低。在还没有更深入研究之前,各位看官有何看法呢?

 

package com.dao;

import java.util.*;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import bo.*;

public class Test {

 AutoInfo ai = new AutoInfo();
 People people = new People();
 public void DoTest() {
  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   people = (People) session.load(People.class, new Integer(1));
   Iterator iterator = people.getAutoInfoSet().iterator();
   System.out.println(people.getName());
   System.out.println(people.getAddress());
   while (iterator.hasNext()) {
    ai = (AutoInfo) iterator.next();
    System.out.println(ai.getLicensePlate());
   }
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}

  

 

package com.dao;

import java.util.*;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import bo.*;

public class Test {

 List list;
 AutoInfo ai = new AutoInfo();
 People people = new People();
 public void DoTest() {
  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   List list =
    session.find(
     "select ai from AutoInfo as ai where ai.OwnerNo.Id=1");
   for (int i = 0; i < list.size(); i++) {
    ai=(AutoInfo)list.get(i);
    System.out.println(ai.getLicensePlate());
    people=ai.getOwnerNo();
    System.out.println(people.getName());
    System.out.println(people.getAddress());
   }
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}

  

 

 

 

package com.dao;

import java.util.*;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import bo.*;

public class Test {

 List list;
 AutoInfo ai = new AutoInfo();
 People people = new People();
 public void DoTest() {
  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   Transaction tx = session.beginTransaction();
   people =
    (People) session
     .find(
      "from People where OWNER_ID=1")
     .get(0);
   ai.setLicensePlate("A00002");
   ai.setOwnerNo(people);
   people.getAutoInfoSet().add(ai);
   session.save(people);
   tx.commit();
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}

 

package com.dao;

import java.util.*;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import bo.*;

public class Test {

 List list;
 AutoInfo ai=new AutoInfo();
    People people=new People();
 public void DoTest() {
  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   Transaction tx = session.beginTransaction();
   
   people.setAddress("中国");
   people.setName("张三");
   people.addToAutoInfoSet(ai);
   ai.setLicensePlate("A00001");
   ai.setOwnerNo(people);
   session.save(people);
   tx.commit();
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}

 

Configuration cfg = new Configuration().configure();

 

 

 

 

 

 

 

 

 

SessionFactory sessions = cfg.buildSessionFactory();

 

 

 

 

 

 

 

 

 

Session session = sessions.openSession();

 

 

 

 

 

 

 

 

 

Transaction tx = session.beginTransaction();

 

 

 

 

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

市场是无情的,机遇和危机无处不在。张三在经历过生意红火之后,接下来的一年内生意场上连连告负,不得不把自己的摊子收缩一下。这第一件事要把跑运输的车卖掉,就是那辆牌照为“A00002”的

package com.dao;

import java.util.*;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import bo.*;

public class Test {

 AutoInfo ai;
 People people;
 public void DoTest() {
  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   Transaction tx = session.beginTransaction();
   ai =
    (AutoInfo) session.find(
     "from AutoInfo where LICENSE_PLATE='A00002'").get(
     0);
   people = ai.getOwnerNo();
   people.getAutoInfoSet().remove(ai);
   session.delete(ai);
   tx.commit();
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}


为什么要从 People 对象中移除某个 AutoInfo 对象?

 

    问得好!传统 JDBC 程序可以直接删除以“A00002”为条件查询出的记录,这样没有问题。但如果在 Hibernate 中用同样的方式直接删除,会引起不小的麻烦:

net.sf.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): 2, of class: bo.AutoInfo


    造成无法删除的原因是PEOPLE AUTO_INFO 表存在着一对多(one-to-many)关系,想要从 AUTO_INFO 删除一条记录,就必须用 people.getAutoInfoSet().remove(ai) 方法为 People 移除以“A00002”为条件查询出的AutoInfo 对象,才能真正删除该 AutoInfo 对象。

 

    从张三的失落中回过头来,这次 Hibernate 之旅也即将结束了。最后要体验的是删除 PEOPLE 表及其关联的 AUTO_INFO 表。

package com.dao;

import java.util.*;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

import bo.*;

public class Test {

 People people;
 public void DoTest() {
  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   Transaction tx = session.beginTransaction();
   people =
    (People) session.load(People.class,new Integer(1));
   session.delete(people);
   tx.commit();
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }
 }
}


    Hibernate 的优势又一次体现出来。我们只需把一对多(one-to-many)关系中“one”这方删除,与之相关联的所有其他记录会一并删除。

 

    最后,通过这次旅程,也算把 Hibernate 的特性体验了一把。作为一种 O/R Mapping 实现,它是很优秀的,希望我们都可以用好它。


《我的 O/R Mapping 之旅》勘误及补充<script language="javascript" type="text/javascript"> document.title="《我的 O/R Mapping 之旅》勘误及补充 - "+document.title </script>
《我的 O/R Mapping 之旅(二)》,有一段对 People.hbm.xml 的分析,谈到为什么使用 inverse=”true”:

在车辆管理系统中,代表着一个拥有者拥有多台车辆。以 java.util.Set 类型表示。 inverse 用于标识双向关联中的被动方一端。inverse=false 的一方(主控方)负责维护关联关系;在车辆管理系统中, AutoInfo 作为主控方,应该把它设为“true”。这就好比你(被动方 one)在某个聚会上散发了许多名片,但是有可能你不清楚接收者(主动方 many)的具体背景;这个不要紧,接收者在必要的时候会和你联系就是了(主动维护关系)。

红色标识的句子容易让人产生歧异,好像是在说把 AtuoInfo 设置成“true”了。应改为:

在车辆管理系统中,AtuoInfo 作为主控方,应该在 People 中设置 inverse =“true”。

沿着思路往下走,你也许会问:什么才叫“主动维护关系”?不妨看看下面的代码(摘自 《我的 O/R Mapping 之旅(三)》):

AutoInfo ai=new AutoInfo();
People people=new People();
public void DoTest() {
   try {
      Configuration cfg = new Configuration().configure();
      SessionFactory sessions = cfg.buildSessionFactory();
      Session session = sessions.openSession();
      Transaction tx = session.beginTransaction();
   
      ai.setLicensePlate("A00001");
      ai.setOwnerNo(people);
      people.setAddress("中国");
      people.setName("张三");
      people.addToAutoInfoSet(ai);
      session.save(people);
      tx.commit();
      session.close();
   } catch (Exception e) {
      System.out.println(e);
   }
}


把“ai.setOwnerNo(people)”注解了试试,由于 AutoInfo 没有主动维护关系,导致 AUTO_INFO 表中 OWNER_NO 字段为“Null”。自然 AutoInfo 与 Poople 就不存在任何联系了。

人类的求知欲很强烈!
为什么非要用 AutoInfo 作为主控方?People 作主控方不行?好吧,为 People.hbm.xml 删除inverse=”true”,再运行以上程序,其实也能保存,只是多了一条SQL:“update auto_info set OWNER_NO=? where AUTO_ID=?”,这就是 AutoInfo 被动地修改和 People 的联系。多执行一次 SQL 意味着多了一些开销,这是对性能不利的!


《我的 O/R Mapping 之旅(三)》,有一段对张三第二次买车的程序和描述:

 AutoInfo ai = new AutoInfo();
 People people = new People();
 public void DoTest() {
  try {
      Configuration cfg = new Configuration().configure();
      SessionFactory sessions = cfg.buildSessionFactory();
      Session session = sessions.openSession();
      Transaction tx = session.beginTransaction();
      people =
       (People) session
        .find(
         "from People where OWNER_ID=1")
        .get(0);
      ai.setLicensePlate("A00002");
      ai.setOwnerNo(people);
      people.getAutoInfoSet().add(ai);
      session.save(people);
      tx.commit();
      session.close();
  } catch (Exception e) {
      System.out.println(e);
  }
}


到这里,也许你会有这样的想法:“应该可以直接向 AUTO_INFO 表插入记录,不通过 People 对象中转,像写 SQL 一样 Easy。” 错了!以前直接写 SQL 是可以办到的,不过现在我们用的可是 Hibernate ,一切都要以对象行事,看见 ai.setOwnerNo(people) 了吗?传入参数是个 People 对象实例,不是简单的字段喔。

这段解释太绝对了,事实上可以直接保存 AutoInfo 对象,而不用通过保存 People 来中转:

AutoInfo ai = new AutoInfo();
People people = new People();
public void DoTest() {
  try {
      Configuration cfg = new Configuration().configure();
      SessionFactory sessions = cfg.buildSessionFactory();
      Session session = sessions.openSession();
      Transaction tx = session.beginTransaction();
      people =
       (People) session
        .find(
         "from People where OWNER_ID=1")
        .get(0);
      ai.setLicensePlate("A00002");
      ai.setOwnerNo(people);
      session.save(ai);
      tx.commit();
      session.close();
  } catch (Exception e) {
      System.out.println(e);
  }
}



《我的 O/R Mapping 之旅(四)》,删除 PEOPLE 表及其关联的 AUTO_INFO 表时,程序是没有错,不过有更简单的办法来删除:

  try {
   Configuration cfg = new Configuration().configure();
   SessionFactory sessions = cfg.buildSessionFactory();
   Session session = sessions.openSession();
   Transaction tx = session.beginTransaction();
   session.delete("from People where OWNER_ID=1");
   tx.commit();
   session.close();
  } catch (Exception e) {
   System.out.println(e);
  }

Hibernate 中,要完成一次操作,可以有多种实现方式,哪种最好,就要靠自己定夺了。



原作者:Rosen Jiang 以及出处: http://blog.csdn.net/rosen


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值