UML软件工程组织

 

 

在 SCA Module 中使用 Hibernate 框架实现数据持久层
2008-07-11 作者:张俊青,李嘉涛 来源:IBM
 
本文内容包括:
本文将探讨在完成 SCA Module 建模后用 Java 对象进行实现时,如何采用 Hibernate 实现 SCA Module 的数据持久层为 SDO 提供数据访问服务,达到充分利用 Hibernate 的优势来加快 SCA 模块实现的方法。

一、背景

SCA(Service Component Architecture)作为服务组件体系结构,将所有的集成构件都描述为具有定义明确的接口的服务组件。SCA 还引入了模块的概念,它将服务组件集中到一起,并提供服务的进一步说明和封装。这意味着只要模块的接口保持不变,就可以在不影响整个解决方案中的任何其他模块的情况下更改模块内的服务组件。服务组件的实现可以是Java 对象(例如POJO或者SLSB), BPEL,Human task,业务状态机以及业务规则集合等。

服务数据对象(Service Data Objects,SDO)是SOA体系结构中的一个用来简化和统一数据应用的开发框架,也是SCA Module的基本组成部分。SDO支持与XML的集成并且适用于J2EE模式和最佳实践。与其他的数据集成模型不同,SDO不仅仅是数据的抽象,SDO框架也是一个断开连接的编程模型,即可以在不连接任何数据源的情况下进行编程。

Hibernate是一种Java语言下的对象关系映射解决方案,Hibernate不仅负责从Java类到数据库表的映射,还包括从Java数据类型到SQL数据类型的映射,并且还提供了面向对象的数据查询检索机制,从而极大地缩短的手动处理SQL和JDBC上的开发时间。

本文主要由两部分内容组成:
1. 讨论如何以Model Driven的方式,构建SCA模块以及生成实现。
2. 讨论如何引入Hibernate实现SCA Module的持久层,并结合示例讨论如何针对类与类之间的各种映射关系定义 Hibernate映射文件,以及如何调用Hibernate API操作JavaBean来实现对数据库对象的访问。

文中涉及到的建模和代码实现都是在WebSphere Integration Developer v6.0开发并基于 WebSphere Process Server v6.0上测试完成的,使用的数据库是DB2 v8.2。

二、先决条件

开始之前,希望读者对如下知识有一定了解:

  • SOA:Service Oriented Architecture 面向服务的体系结构
  • SCA:Service Component Architecture 服务组件体系结构
  • SDO:Service Data Object 服务数据对象
  • Hibernate:一种Java语言下的对象关系映射解决方案

三、创建示例项目

1. 示例项目背景介绍

我们引入一个证券业的简单实例来说明整个过程。

如果您想从事证券投资,就需要去证券公司开立一个交易账户。开户时,证券公司需要把您的个人信息注册到股东表中,并且对您购买的股票进行记录以进行后续的证券买卖结算。基于这个场景, 我们将基于SCA把建立个人证券账户相关的业务功能构建为服务组件,在实现服务组件时采用Hibernate来快速完成数据对象与SDO之间的映射工作。

我们用以下的用例图来描述这个场景:

图-1:用例图
图-1:用例图

根据这个场景,我们需要为股东,证券,证券类别,股东证券账户等实体创建相应的Java对象,用下面的类图来描述这些 Java对象以及它们之间的关系:

图-2:类图
图-2:类图

在创建图-2所示的类图时,我们同时也创建了对应的Java类。这种Model-Drive的开发方式,省却了再次手动创建Java类的繁琐,并且可以实现类图和代码上对Java类进行同步更改。

根据上面的背景介绍,为了构建一个服务组件来提供与建立个人证券账户相关的业务功能的服务,我们将构建一个名为 StockService的组件,该组件实现以下业务功能:

清单-1:业务功能列表
 
名称 功能
addStock 新增一个证券
addStockType 新增一个证券类别
addStockAccount 新增一条证券账户信息
addStockHolder 新增一个股东
getStockList 获得某个股东的所有证券信息列表
getStockAccountListByStockId 根据证券代码获得具有该证券的股东账户列表
getStockTypeList 获得所有的证券类别列表

为此,我们按照以下步骤创建Module项目以及相关的SDO和Interface。

2. 创建StockModule项目

我们为StockService服务组件示例创建一个新的workspace,例如,本文所用示例的workspace路径为: d:\workspace\soa。然后,切换到Business Integration视图下。

选择菜单File->New->Others打开新建项目窗口,选择Business Integration菜单中的Module,如下图所示:

图-3:新建Module窗口
图-3:新建Module窗口

将新建的Module命名为:StockModule,如下图所示:

图-4:新建Module窗口
图-4:新建Module窗口

3. 创建SDO

作为构建StockService的基础,我们须根据类图定义模块所需的SDOs:

清单-2:需要创建的SDO属性列表
 
SDO名称 属性 SDO字段类型 说明
StockType:证券类别 id string 证券类别代码
  stkTypeName string 证券类别名称
StockTypeList:证券类别列表 stockTypes[] StockType  证券类别列表
Stock:证券 id string 证券代码
  stkName string 证券名称
  stkType StockType 证券类别
StockList:证券列表 stocks[] Stock 证券列表
StockMemo:证券相关信息 stock Stock 证券
  currentPrice double 当前价格
  highestPrice double 最高价
  lowestPrice double 最低价
StockHolder:股东信息 id string 股东代码
  name string 股东姓名
  gender string 性别
  idCardNum string 身份证号码
  email string 电子邮箱
  address string 地址
StockAccount:股东-证券账户信息 stockHolder StockHolder 股东信息
  stock Stock 股东所拥有的证券信息
  balance int 当前余额
  profit double 盈余
  stockMemo StockMemo 证券价格信息

比较复杂的是StockAccount,下图反映了StockAccount与StockHolder,Stock,StockMemo之间的引用关系:

图-5:SDO之间的关系
图-5:SDO之间的关系

4. 创建StockServiceInterface

该接口将定义清单-1中所列的业务功能,我们将该接口命名为:StockServiceInterface,创建完毕的接口如下图所示:

图-6:StockServiceInterface接口定义
图-6:StockServiceInterface接口定义

StockModule项目在Business Integration视图下犹如下图所示:

图-7:StockModule项目资源情况
图-7:StockModule项目资源情况

5. 为StockModule增加接口

双击图-7中的红色部分,打开StockModule的Assembly Diagram,右键单击StockService,在弹出菜单中选择Add->Interface,将StockServiceInterface加入到StockService组件中。

图-8:StockModule的Assembly Diagram:
图-8:StockModule的Assembly Diagram:

6. 为StockService组件导出WS绑定

图-8所示的Assembly Diagram中,在StockService组件上点右键弹出的菜单中,选择export分别为该组件导出Web service绑定,命名为StockServiceExport,然后为StockService增加一个Stand-alone References,以使得StockService 组件具有为本地客户端提供服务的能力。

图-8:为StockService导出的Web service绑定
图-8:为StockService导出的Web service绑定

7. 自动生成Module的Java实现代码

图-8所示的Assembly Diagram所示的StockService组件上点右键弹出的菜单中,选择Generate Implementation (如果已经生成过Java实现,则为Regenerate Implementation)来自动生成Java实现代码。

图-10:自动生成Java实现代码
图-10:自动生成Java实现代码

在弹出的窗口中,为生成的代码选择所在的package。图-11中所示,请选择module.stock.implementation:

图-11:为即将生成的代码选择package
图-11:为即将生成的代码选择package

生成以后的Assembly Diagram有一些变化,在StockService组件的图示上会增加一个J的标志,表示是Java implementation, 如图-12所示:

图-12: 生成Java实现后的Assembly Diagram
图-12: 生成Java实现后的Assembly Diagram

四、引入Hibernate实现StockService组件

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以使用面向对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序实用,也可以在Servlet/JSP的Web应用中使用。本文讨论的在对SCA模块的实现时,我们使用Hibernate实现SCA的持久层。

使用Hibernate之后,StockService模块,SDO,Java Bean, DB之间的关系可以总结为以下图示:

图-13
图-13

1.下载Hibernate

您可以到http://www.hibernate.org网站上下载Hibernate。本文的示例使用的是3.2.0版本,您可以到下列地址下载hibernate-3.2.0.ga.zip:
http://prdownloads.sourceforge.net/hibernate/hibernate-3.2.0.ga.zip?download 下载后解压到本地磁盘。

2.在StockModule项目中配置Hibernate运行环境

在第一部分,我们已经为StockService模块生成实现代码,我们需要将其中的每个方法予以实现。实现类名为: module.stock.implementation.StockServiceImpl,我们将把相关的Java对象和Hibernate对象放到同一级路经下,即:module.stock.implementation,这个包所在的文件夹路径为:

StockModule\gen\src\module\stock\implementation

将运行Hibernate所需要的jar添加到Java Build Path中

在StockModule项目文件夹中新建一个lib目录,将以下jar包从hibernate解压之后的目录拷贝到StockModule\lib目录下:

  • antlr-2.7.6.jar
  • asm.jar
  • cglib-2.1.3.jar
  • commons-collections-2.1.1.jar
  • commons-logging-1.0.4.jar
  • dom4j-1.6.1.jar
  • hibernate3.jar
  • jta.jar
  • log4j-1.2.11.jar

然后,在StockModule项目的Properties中,点击”Add JARs”按钮,将这些包添加到StockModule项目的Java Build Path中,添加以后如图-14所示:

图-14:将Hibernate所需的Jar包添加到Java Build Path中
图-14:将Hibernate所需的Jar包添加到Java Build Path中

配置数据库连接信息

将StockModule发布到WID中的Websphere Process Server v6.0,然后在管理控制台增加名称为jdbc/localdb的JNDI配置,并保证测试连接成功:

Step1: 设置DB2_JDBC_DRIVER_PATH,指定DB2 JDBC驱动的位置
例如:C:\Program Files\IBM\SQLLIB\java

Step2: 新建J2C认证信息
在Security -> Global security -> JAAS Configuration -> J2C Authentication data中,新增J2C认证信息,这里的用户名和密码是为登录数据用的。

图-15:新增J2C认证数据
图-15:新增J2C认证数据

Step3: 新增JDBC Providers:DB2 Legacy CLI-based Type 2 JDBC Driver
选择Resources -> JDBC Providers 菜单,点击New按钮:

图-16:新增JDBC Providers
图-16:新增JDBC Providers

保存之后,在其Data Sources属性中,新增jndi_localdb的JNDI配置:

图-17:新增jndi_localdb的JNDI配置
图-17:新增jndi_localdb的JNDI配置

在module.stock.implementation目录中创建hibernate.cfg.xml并进行配置

主要配置以下几项:
hibernate.connection.datasource:jdbc/localdb(JNDI名称)
connection.pool_size:1
dialect:org.hibernate.dialect.DB2Dialect
show_sql:true(将Hibernate生成的SQL语句以SystemOut的方式输出到日志中)
mapping resource:引入Hibernate映射文件
配置完毕的hibernate.cfg.xml文件如下表:

清单-3:hibernate.cfg.xml文件内容
 
                
<?xml version='1.0' encoding='utf-8'?>                                            
<!DOCTYPE hibernate-configuration PUBLIC                                             
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"                    
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>                                                         
    <session-factory>                                                             
        <!-- Database connection settings, we use jndi here -->                   
        <property name="hibernate.connection.datasource">               
			jdbc/localdb</property>                                                           
        <property name="connection.pool_size">1</property>        
        <!-- SQL dialect -->                                                      
        <property name="dialect">                                       
			org.hibernate.dialect.DB2Dialect</property>                                       
        <!-- Enable Hibernate's automatic session context management -->          
        <property name="current_session_context_class">                 
			thread</property>                                                                 
        <!-- Disable the second-level cache  -->                                  
        <property name="cache.provider_class">                          
			org.hibernate.cache.NoCacheProvider</property>                                    
        <!-- Echo all executed SQL to stdout -->                                  
        <property name="show_sql">true</property>                 
        <mapping resource=                                                           
			"/gen/src/module/stock/implementation/Stock.hbm.xml"/>                     
        <mapping resource=                                                           
			"/gen/src/module/stock/implementation/StockType.hbm.xml"/>                 
        <mapping resource=                                                           
			"/gen/src/module/stock/implementation/StockAccount.hbm.xml"/>              
        <mapping resource=                                                           
			"/gen/src/module/stock/implementation/StockHolder.hbm.xml"/>               
        <mapping resource=                                                           
			"/gen/src/module/stock/implementation/StockMemo.hbm.xml"/>                 
    </session-factory>                                                            
</hibernate-configuration>                                                        

该文件将在实例化SessionFactory的时候被引用,Hibernate将根据此文件创建数据库连接池,并根据其中的mapping 定义建立*.hbm.xml与JavaBean之间的映射关系。有关SessionFactory,请参考实现StockService模块一节的内容。

3.连接到sample数据库并创建所需数据表

在本文所附的StockModule.rar中,在StockModule\gen\src\module\stock\implementation路径下包含一个名为 create_db_objects.sql的文件,运行db2cmd并连接到sample数据库上,

db2 connect to sample

然后使用下列命令提交create_db_objects.sql文件来创建所需的表:

db2 –td; -f create_db_objects.sql

项目中还有一个init_db_objects.sql文件来向所创建的表中插入一些测试数据,请执行下列命令:

db2 –td; -f init_db_objects.sql

4.创建Java类

在创建图-2所示的类图的同时,我们已经创建了下列JavaBean:

  • module.stock.implementation.StockType
  • module.stock.implementation.StockMemo
  • module.stock.implementation.StockHolder
  • module.stock.implementation.StockAccount
  • module.stock.implementation.Stock

使用WebSphere Integration Developer,程序员能够以模型驱动的方式在建模的过程中创建类图和对应的Java类。

5.创建Hibernate映射文件

Hibernate映射文件定义的是数据库表的字段和JavaBean的属性之间的映射关系,这种映射关系除了一般字段与字段的直接对应关系外,还包括表与表之间的多对一,一对多,多队多的对应关系。Hibernate将根据这些映射文件,自动生成SQL语句,并把从数据库中取得的对象填充到JavaBean的简单属性或复杂属性中。

这一节中,我们将介绍如何为本例中的各个JavaBean构造Hibernate映射文件,并将揭示以下技巧:

  • 如何映射id主键以及id值的生成方法
  • 如何使用数据库提供的sequence对象生成id
  • 如何映射多对一关系
  • 如何定义组合主键映射

创建StockType.hbm.xml – 最一般的情形

StockType是最简单的JavaBean,没有复杂的映射关系,StockType.hbm.xml定义StockType这个JavaBean和数据库表StockType之间的映射关系:

清单-4:stockType表定义
 
                
CREATE TABLE stockType(
  id                VARCHAR(10) NOT NULL,
  typeName          VARCHAR(20),
  CONSTRAINT pk_stockType PRIMARY KEY(id)
);

清单-5:StockType类定义
 
                
package module.stock.implementation;
import java.io.Serializable;
public class StockType implements Serializable {
    private String id;
    private String typeName;
    //Getters and setters…
    … …
}

清单-6:StockType.hbm.xml定义
 
                
<?xml version="1.0"?>                                            
<!DOCTYPE hibernate-mapping PUBLIC                               
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"                 
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="module.stock.implementation">        
    <class name="StockType" table="StockType" 
        lazy="true">       
        <comment>Stock Type table</comment>                      
        <id name="id">                                           
            <generator class="assigned" />                       
        </id>                                                    
        <property name="typeName"/>                              
    </class>                                                     
</hibernate-mapping> 

- package: 指明 class name=”StockType”所代表的JavaBean的package路径。
- table: 指明与StockType JavaBean建立映射关系的表名。
- comment: 对此映射文件的注释
- id: 主健定义,必须唯一,如果StockType表中的字段名不是id,则需要用column=”idName”来说明,例如:<id name="id" column="idName">
- generator: 指定主键值的产生算法,assigned为用户指定。可供选择的还有: sequence(使用数据库提供的sequence对象,Oracle, DB2等数据库支持,本例将用到sequence), increment, identity, hilo, seqhilo, uuid, guid, native,select foreign等,关于这些id生成算法的具体情况,请参考Hibernate reference documentation。
- property:指定JavaBean属性与数据表字段之间的映射,如果列名与JavaBean属性名不相同,则需要增加column=”” 来说明。如<property name="typeName" column="name"/>。

创建Stock.hbm.xml – 定义多对一关系

Stock(证券)类有一个stkType属性,类型为StockType(证券类别),一个证券类别可以有多个证券,多个证券可能属于同一个证券类别,所以它们之间的关系是多(Stock)对一(StockType)的关系,我们使用many-to-one来定义这种关系。其他字段直接映射即可。

清单-7:stock表定义
 
                
CREATE TABLE stock(
  id                  VARCHAR(10) NOT NULL,
  stkName             VARCHAR(40),
  stkTypeId           VARCHAR(10),
  CONSTRAINT pk_stock PRIMARY KEY (id)
);

清单-8:Stock类定义-6:StockType.hbm.xml定义
 
                
package module.stock.implementation;
import java.io.Serializable;
public class Stock implements Serializable {
    private String id;
    private String stkName;
    private String stkTypeId;
    private StockType stkType;
    //Getters and setters
    ……
}

清单-9:Stock.hbm.xml定义
 
                
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="module.stock.implementation">
    <class name="Stock" table="Stock" lazy="true">
        <comment>Stock table</comment>      
        <id name="id">
            <generator class="assigned" />
        </id>       
        <property name="stkName"/>
        <property name="stkTypeId" />
        <many-to-one name="stkType" class="StockType"
            insert="false" update="false">
            <column name="stkTypeId"/>
        </many-to-one>
    </class>    
</hibernate-mapping>

-many-to-one:定义stkType属性与StockType类之间的多对一关系。Name为Stock类的stkType属性,class指定该属性的类型, <column>指定在Stock表中与StockType进行连接的属性名,在本例中为stkTypeId。

创建StockHolder.hbm.xml – 使用数据库的sequence

StockHolder存放股东信息,该表中的主键id的值由一个用户定义的sequence产生,为了让Hibernate能够自动调用这个sequence的 nextval获取下一个值,我们必须指定id元素的generator的class为sequence,并且在param中指定sequence的名称,如清单-10所示:

清单-10:StockHolder表定义
 
                
CREATE TABLE StockHolder(
  id                INTEGER NOT NULL, 
  name              VARCHAR(40), 
  gender            CHAR(2),     
  idCardNum         VARCHAR(18), 
  email             VARCHAR(50), 
  address           VARCHAR(100),
  CONSTRAINT pk_stockHolder PRIMARY KEY (id)
);
CREATE SEQUENCE stockHolder_seq
    START WITH 1005
    INCREMENT BY 1
    NOMAXVALUE
    NOCYCLE
    CACHE 24 ;

清单-11:StockHolder类定义
 
                
public class StockHolder implements Serializable {
    private String id;
    private String name;
    private String gender;
    private String idCardNum;
    private String email;
    private String address;
//Getters and setters
……
}

清单-12:StockHolder.hbm.xml定义
 
                
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="module.stock.implementation">
    <class name="StockHolder" table="StockHolder" 
        lazy="true">
        <comment>Stock Holder table</comment>       
        <id name="id">
            <generator class="sequence">
                <param name="sequence">stockHolder_seq</param>
            </generator>
        </id>       
        <property name="name"/>
        <property name="gender"/>
        <property name="idCardNum"/>
        <property name="email"/>
        <property name="address"/>
    </class>    
</hibernate-mapping>

StockMemo.hbm.xml – 使用identity

StockMemo表存放每个交易日的证券价格信息,StockMemo表的主键id在数据库中是一个identity的自增字段类型,因此在做Hibernate映射时,必须使用<generator class="native" />来对id的generator算法进行定义, native将使得Hibernate在插入数据时,使用数据库内置的identity属性来生成唯一的id值。

清单-13:stockMemo表定义
 
                
CREATE TABLE stockMemo(
  id                INTEGER NOT NULL
                    GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
  stockId           VARCHAR(10) NOT NULL, 
  currentPrice      DECIMAL(17,3),
  highestPrice      DECIMAL(17,3),
  lowestPrice       DECIMAL(17,3),
  tradeDate         VARCHAR(10) default char(current date),
  FOREIGN KEY fk_stockMemeo (stockId)
          REFERENCES Stock ON DELETE NO ACTION
);

清单-14:StockMemo类定义
 
                
public class StockMemo implements Serializable {
    private String id;
    private String stockId;
    private String currentPrice;
    private String highestPrice;
    private String tradeDate;
private Stock stock;
//Getters and setters
……
}

StockMemo类还有一个stock属性,类型为Stock,很明显对应于stockId,StockMemo与Stock之间是多对一的关系,因此,我们使用 many-to-one元素来定义stock属性。如清单-13所示:

清单-15:StockMemo.hbm.xml定义
 
                
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="module.stock.implementation">
    <class name="StockMemo" table="StockMemo" 
        lazy="true">
        <comment>Stock memo table</comment>     
        <id name="id">
            <generator class="native" />
        </id>
        <property name="stockId"/>
        <property name="currentPrice"/>
        <property name="highestPrice"/>
        <property name="lowestPrice"/>
        <property name="tradeDate"/>
        <many-to-one name="stock" class="Stock"
            insert="false" update="false">
            <column name="stockId"/>
        </many-to-one>
    </class>    
</hibernate-mapping>

StockAccount.hbm.xml – 使用compose-id和多个many-to-one映射

StockAccount类比较复杂,对应的Accout表存放的是股东拥有证券的情况,除了要对stockHolderId, stockId, balance, profit等四个一般属性进行映射外,还必须为三个复杂数据类型的属性进行映射。

在定义映射关系之前,需要明确StockAccount与StockHolder,Stock以及StockMemo之间的对应关系:

  • 一个股东可以买卖多个证券,因此StockAccount与StockHolder之间是多对一关系
  • 一个证券可以被多个股东所买卖,因此StockAccount与Stock之间是多对一关系
  • 一个证券在不同的交易日有不同的价格,因此StockMemo与Stock之间是多对一关系
  • 值得注意的是通过StockAccount,我们实际上定义了StockHolder与Stock之间的多对多关系。
清单-16:Account表定义
 
                
CREATE TABLE account(
  stockHolderId INTEGER NOT NULL,
  stockId           VARCHAR(10) NOT NULL,
  balance           INT,
  profit            DECIMAL(17,3),
  FOREIGN KEY fk_account1 (stockHolderId)
          REFERENCES StockHolder ON DELETE NO ACTION,
  FOREIGN KEY fk_account2 (stockId)
          REFERENCES stock ON DELETE NO ACTION
);

对于Account表,stockHolderId和stockId组合可以被视为该表的主键,这一点将使用composite-id元素在Hibernate映射文件中进行表达。

清单-17:StockAccount类定义
 
                
public class StockAccount implements Serializable {
    private String stockHolderId;
    private String stockId;
    private int balance;
    private float profit;
    private StockHolder stockHolder;
    private Stock stock;
    private StockMemo stockMemo;
    //Getters and setters
	......
    public boolean equals(StockAccount sa){
        boolean isEquals=false;
        if(sa!=null){
            if(stockHolder.getId().equals(sa.getStockHolder().getId()) &&
                    stockHolder.getIdCardNum().equals(
                        sa.getStockHolder().getIdCardNum()) &&
                        stock.getId().equals(sa.getStock().getId()) &&
                        stock.getStkName().equals(sa.getStock().getStkName())       
                )
                isEquals = true;
        }
        return isEquals;
    }
    public int hashCode() {
        int h=0;
        h = stockHolder.hashCode()+19*stock.hashCode();
        return h;
    }
}

清单-18:StockAccount.hbm.xml定义
 
                
<a name="<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="module.stock.implementation">
	<class name="StockAccount" table="Account" lazy="true">
		<comment>Stock Account table</comment>		
		<composite-id >
			<key-property name="stockHolderId"/>
			<key-property name="stockId"/>
		</composite-id>		
		<many-to-one name="stockHolder" class="StockHolder"
			insert="false" update="false">
			<column name="stockHolderId"/>
		</many-to-one>
		<many-to-one name="stock" class="Stock"
			insert="false" update="false">
			<column name="stockId"/>
		</many-to-one>		
		<many-to-one name="stockMemo" class="StockMemo"
			insert="false" update="false">
			<column name="stockId"/>
		</many-to-one>		
		<property name="balance"/>
		<property name="profit"/>
	</class>	
</hibernate-mapping>
"></a>

-composite-id定义了一个由stockHolderId和stockId组成的组合主键。和一般id不同,在包含composite-id的持久类中,必须重载equals()和hashCode()这两个方法。如上面的清单-15所示。

-三个many-to-one元素,把stockHolder, stock和stockMemo分别映射到StockHolder类,Stock类和StockMemo类上。

6.实现StockService模块

StockServiceImp是StockService模块的实现类,我们使用Hibernate充当数据访问服务层,通过JavaBean可以很方便的实现底层数据与SDO之间的交换。

图-18 SDO,JavaBean与数据库的交互过程
图-18 SDO,JavaBean与数据库的交互过程

首先,我们需要创建一个叫做HibernateUtil的类,如清单-17所示,它以Singleton的模式实例化一个SessionFactory,为Hibernate API调用提供基础。在实例化SessionFactory时,指定hibernate.xfg.xml配置文件(见清单-3)。

清单-19: HibernateUtil.java
 
                
public class HibernateUtil {
    private static final SessionFactory sessionFactory;
    static {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            sessionFactory = new Configuration()
                .configure("/gen/src/module/stock/implementation/hibernate.cfg.xml")
                .buildSessionFactory();
        } catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

然后,实现addStock, addStockType,getStockTypeList等一系列方法。限于篇幅,本文只介绍两个典型的方法 addStockAccount和getStockList的实现,这两个方法的实现包括了实现其他几个方法会涉及到的问题。

1) addStockAccount方法:

该方法的功能是:将StockHolder SDO和Stock SDO以及stock的数量qty等数据存储到account表中,业务上表现为股东StockHolder买进数量为qty的Stock类证券。

输入参数有三个:

  • DataObject stockHolder, 股东信息
  • DataObject stock,证券信息
  • Integer qty,买入的证券数量

我们的方法是把DataObject中的数据转换到JavaBean中,然后再利用Hibernate,把数据持久化到DB中,代码如清单-18所示:

清单-20:addStockAccount的实现代码
 
                
public String addStockAccount(DataObject stockHolder, DataObject stock,Integer qty) {
    String message = null;
    if (stockHolder != null && stock != null) {
        try {//得到SessionFactory的实例
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            //这一句是必需的,表明Transaction的开始
            session.beginTransaction();
            //将输入参数DataObject分别转换为JavaBean
            StockHolder sh = convertToStockHolder(stockHolder);
            Stock st = convertToStock(stock);
            //新建一个StockAccount Bean对象
            StockAccount sa = new StockAccount();
            //把入参都set进去
            sa.setStock(st);
            sa.setStockHolder(sh);
            sa.setStockHolderId(sh.getId());
            sa.setStockId(st.getId());
            sa.setBalance(qty.intValue());
            sa.setProfit(0); //初始化为0
            //调用session.persist()方法,指定StockAccount将被持久化
            session.persist("StockAccount", sa);
            //调用session.flush()方法将memory中的数据保存到数据库中
            session.flush();
            //关闭session
            session.close();
            message = "StockAccount saved successfully!";
        } catch (Exception e) {
            e.printStackTrace();
            message = "Faile to save StockAccount to database: "+ e.getMessage();
        }
    }
    return message;
}

下面对上述代码进行简要说明:

  • 首先,我们把输入参数中的两个DataObject转换为JavaBean:sh(StockHolder)和st(Stock)

对于StockHolder,我们定义以下方法来完成DataObject向JavaBean转化:

清单-21:convertToStockHolder(DataObject stockHolderBO)方法
 
                
private StockHolder convertToStockHolder(DataObject stockHolderBO) {
    StockHolder stockHolder = null;
    if (stockHolderBO != null) {
        try {
            stockHolder = new StockHolder();
            stockHolder.setId(stockHolderBO.getString("id"));
            stockHolder.setName(stockHolderBO.getString("name"));
            stockHolder.setGender(stockHolderBO.getString("gender"));
            stockHolder.setIdCardNum(stockHolderBO.getString("idCardNum"));
            stockHolder.setEmail(stockHolderBO.getString("email"));
            stockHolder.setAddress(stockHolderBO.getString("address"));
        } catch (Exception e) {
            e.printStackTrace();
            stockHolder = null;
        }
    }
    return stockHolder;
}

同样,为Stock定义一个方法实现DataObject与JavaBean之间的相互转化,这里从略。

  • 然后创建一个StockAccount的实例,并把StockHolder的实例sh和Stock的实例st 放到StockAccount类中。
  • 最后调用Hibernate的Session.persiste等方法将StockAccount数据存储到数据库中。

清单-22所列的代码体现了Hibernate快速实现JavaBean到数据库持久层转化的能力。这里见不到任何与JDBC相关的操作,只是针对 JavaBean的OO操作,因为Hibernate已经为我们完成了底层的JDBC API调用。

清单-22:调用Hibernate API持久化JavaBean
 
                
1     Session session = HibernateUtil.getSessionFactory().getCurrentSession();
2     session.beginTransaction();
3     StockAccount sa = new StockAccount();
…     sa.setXXX(…)
4     session.persist("StockAccount", sa);
5     session.flush();
6     session.close();

如果StockHolder所包含的股东信息在数据库中不存在,则在调用session.persist(“StockAccount”,sa);之前,需要调用以下方法,先将新的StockHolder信息存到数据库中:

session.persist(“StockHolder”,sh);
可以用下列方法判断StockHolder所包含的股东信息是否已经存在:
if(session.load(StockHolder.class, st.getId()) == null)

2) getStockList方法 – 演绎DB-JavaBean-SDO之路

该方法的功能是:根据股东代码idNum得到属于该股东的所有证券信息的详细列表,如清单-21所示:

清单-23: getStockList(String idNum)定义
 
                
    public DataObject getStockList(String idNum) {
        DataObject stockListBO = null;
        DataObject stockBO = null;
        ArrayList stockList = new ArrayList();
        if (idNum != null) {
            try {
                stockListBO = boFactory.create("http://StockModule/sdo", "StockList");
                Session session = HibernateUtil.getSessionFactory().getCurrentSession();
                session.beginTransaction();
                Iterator iter;
                iter = session.createQuery(
                   "select sa.stock from StockAccount sa where sa.stockHolder.id = ?")
                   .setString(0, idNum).iterate();
                while (iter.hasNext()) {
                    Stock stock = (Stock) iter.next();
                    stockBO = convertToStockBO(stock);
                    stockList.add(stockBO);
                }
                stockListBO.setList("stocks", stockList);
                session.close();
            } catch (Exception e) {
                e.printStackTrace();
                stockListBO = null;
            }
        }
        return stockListBO;
    }

-boFactory是com.ibm.websphere.bo.BOFactory的一个实例,在StockServiceImpl的initialize方法中进行实例化, BOFactory是Business Object的实例化工厂,用来创建BO的实例。

7.模块测试

完成StockService的实现后,使用WID提供的Module Test工具对模块进行测试。在Assembly Diagram中,点击StockService 模块右键菜单中的"Test Component",即可打开如图-19所示的模块测试界面,然后选择对应的操作并填写request入参,再点"continue"按钮运行测试。图-20显示了测试结果。

图-19:选择被测试方法并且填写request参数
图-19:选择被测试方法并且填写request参数
图-20:测试结果
图-20:测试结果

五、总结

本文结合示例详细探讨了使用Hibernate构建SCA Module持久层的方法,并且在创建示例的过程中,使用了基于模型驱动的设计方法构建SCA Module和Java实现。

本文在使用Hibernate构建SCA Module的持久层时,结合示例探讨了处理O/R映射的各种情况的方法,包括如何正确定义id的 generator,如何定义many-to-one映射关系,如何定义composite-id,如何使用数据库内置的sequence和identity属性等。

在用Java对象实现SCA Module时,使用Hibernate构建SCA的持久层可以解耦SCA Module的Java实现和DBMS类型间的关系:通过修改配置 Hibernate的配置文件(hibernate.cfg.xml),即可与不同类型的数据库进行交互,而不用修改Java代码,这一特性可以使得 SCA Module的松耦合特性进一步加强。

下载

描述 名字 大小 下载方法
本文使用的示例代码

StockModule.zip

3954KB

HTTP

参考资料

 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号