内容


在 SCA Module 中使用 iBATIS 框架实现数据持久层

Comments

在完成 SCA Module 建模后用 Java 对象进行实现时,采用 Hibernate 和采用 iBATIS 实现 SCA Module 的数据持久层,目的都是为 SDO 提供数据访问服务并加快 SCA 模块实现。前文已经讲过关于如何使用 Hibernate 实现 SCA Module 的数据持久层,本文将介绍 iBATIS 框架,比较 iBATIS 和 Hibernate 的异同,并以实例的方式介绍如何使用 iBATIS 实现 SCA Module 的数据持久层。

iBATIS 是一种数据映射框架,其 SQL Map 所提供的映射功能能够大大减少访问关系数据库的代码。iBATIS 的 SQL Map 使用简单的 XML 配置文件将 Java Bean 映射成 SQL 语句,对比其他的数据库持久层和 ORM 框架(如 JDO 的实现,Hibernate 等),iBATIS SQL Map 最大的优点在于它简单易学,不需要掌握在 Hibernate 中所必需的表之间复杂的映射关系。要使用 iBATIS SQL Map,只要熟悉 Java Bean,XML 和 SQL,就能使您充分发挥 SQL 语句的能力。

而 iBATIS SQL Map 所提供的 API 让开发人员可以轻易地将 Java Bean 映射成 PreparedStatement 的输入参数和 ResultSet 结果集。开发 SQL Map 的想法很简单:提供一个简洁的架构,能够用 20%的代码实现 80%JDBC 的功能。

iBATIS 与 Hibernate 相比,就一句话,那就是 iBATIS 更直接,更自然。

本文主要内容由两部分组成 :

1. 介绍 SCA 模块,导入模块项目

2. 讨论如何引入 iBATIS 实现 SCA Module 的持久层 , 并结合示例讨论如何针对类与类之间的各种映射关系定义 iBATIS 映射文件,以及如何调用 iBATIS SQL Mapper API 操作 JavaBean 来实现对数据库对象的访问。

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

注意: 本文所述方法和实践均为个人研究成果和经验介绍,并非 IBM 官方推荐解决方案。

先决条件

开始之前,希望读者有如下基本知识:

  • SOA:Service Oriented Architecture 面向服务的体系结构。
  • SCA:Service Component Architecture 服务组件体系结构
  • SDO:Service Data Object 服务数据对象
  • iBATIS:一种 Java 语言下的对象关系映射解决方案,最大的特点就是简单易学。

导入示例项目

1.示例项目背景介绍

我们仍然使用与前文相同的一个例子来说明整个过程。

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

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

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

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

清单 -1:业务功能列表

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

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

2.导入 StockModule 项目

在上一篇文章中,我们已经介绍了如何创建 StockModule 项目,请下载资源列表中的项目交换文件并将其导入您的工作空间,在导入时,请选择“项目交换文件 (Project Interchange)”为导入类型。

项目导入后的项目在 Business Integration 透视图下如下图所示:

图 -2: StockModule 的业务集成视图
图 -2: StockModule 的业务集成视图
图 -2: StockModule 的业务集成视图

您将看到用例中需要的 SDO 和接口已经被创建。

此项目在 Java 透视图下,项目结构为:

其中:StockModule 为手动创建的项目,其他三个项目(即 StockModuleApp,StockModuleEJB 和 StockModuleWeb)都是有 WID 自动生成。

引入 iBATIS 实现 StockService 组件

通过上面的步骤,我们已经为 StockService 组建生成 Java 实现框架:StockServiceImpl.java。接下来我们就要实现此类中的所有方法。其中最重要的一个工作,就是在 SDO 和数据库之间建立一座桥梁,实现数据的来往。

使用 iBATIS 数据映射框架可以简化 Java 或 dotNet 的数据库交互工作。iBATIS 使用 XML 来将存储过程或 SQL 语句和对象联系起来。对于使用大量 SQL 的应用来说,使用 iBATIS 是一个不错的选择。

使用 iBATIS 之后,StockService 模块,SDO,Java Bean, DB 之间的关系可以如下图所示:

图 -3:iBATIS 工作的上下文
图 -3:iBATIS 工作的上下文
图 -3:iBATIS 工作的上下文

1.下载 iBATIS

本文的示例使用的是 2.3.0 版本,您可以到下列地址下载 ibatis-2.3.0.677.zip:

http://ibatis.apache.org/javadownloads.cgi

下载后解压到本地磁盘。

2.配置 iBATIS 工作环境

对于 SCA 项目,配置 iBATIS 的工作环境分三步:

  • 配置编译环境:把相关的 JAR 文件配置到 Java 构建路径类路径下即可
  • 配置运行环境:把相关的 JAR 文件复制到 StockModuleWeb 项目的 /WEB-INF/lib 目录
  • 配置数据库连接参数

下面我们详细介绍这三个步骤:

1)将 iBATIS 的 JAR 文件及其依赖的 JAR 类库文件配置在 Java Build Path 中

在 StockModule 项目文件夹中新建一个 lib 目录,将 ibatis-2.3.0.677.jar 文件从 iBATIS 解压之后的 lib 目录拷贝到 StockModule\lib 目录下。

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

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

注意,因为我们将要操作 SDO,所以必须添加 xsd.bean.runtime.jar 到构建路径中,您可以在 WID 的下列目录中找到这个文件:

  • [INSTALL_DAR]\rwd\eclipse\plugins\com.ibm.etools.ctc.xsd.bean_6.0.2.2\runtime。

2)把下列相关的文件复制到 StockModuleWeb 项目的 WebContent\WEB-INF\lib\ 目录下

为了支持 StockModule 的运行,我们需要把它所依赖的几个 JAR 包拷贝到 StockModuleWeb 项目的 WebContent\WEB-INF\lib\ 目录下,lib 目录默认是没有的,您需要手动创建,这些 JAR 包有:

  • hsqldb.jar
  • ibatis-2.3.0.677.jar
  • xsd.bean.runtime.jar

3)配置数据源

iBATIS 的配置文件是一个 XML 文件,其文件名可以由用户指定,例如 sql-map-config.xml,在此文件中我们可以统一通过 dataSource 的 type 属性来配置 DataSource 的不同实现,iBATIS 支持的实现有:

  • SIMPLE:SimpleDataSourceFactory 为 DataSource 提供了一个基本的实现,适用于在没有 J2EE 容器提供 DataSource 的情况。它基于 iBATIS 的 SimpleDataSource 连接池实现。
  • Jakarta DBCP: 实现使用 Jakarta DBCP(Database Connection Pool)的 DataSource API 提供连接池服务。适用于应用 /Web 容器不提供 DataSource 服务的情况,或执行一个单独的应用。
  • JNDI: 通过 JNDI 上下文查找的 DataSource(即应用服务器中的 DataSource)。

在本例中,我们将使用 SimpleDataSource 类来获取数据库连接。

清单 -2 :sql-map-config.xml 文件内容:

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
 "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>

 <properties resource="module/stock/implementation/database.properties"/>

 <transactionManager type="JDBC">
 <dataSource type="SIMPLE">
 <property value="${driver}" name="JDBC.Driver"/>
 <property value="${url}" name="JDBC.ConnectionURL"/>
 <property value="${username}" name="JDBC.Username"/>
 <property value="${password}" name="JDBC.Password"/>
 </dataSource>
 </transactionManager>

 <sqlMap resource="module/stock/implementation/service/Stock.xml"/>
 <sqlMap resource="module/stock/implementation/service/StockAccount.xml"/>
 <sqlMap resource="module/stock/implementation/service/StockHolder.xml"/>
 <sqlMap resource="module/stock/implementation/service/StockType.xml"/>

</sqlMapConfig>

其中引用了位于 module/stock/implementation/ 目录下的 database.properties 文件,在该文件中,我们可以指定 JDBC 驱动,数据库 URL,用户名和密码:

清单 -3: database.properties 文件内容

driver=org.hsqldb.jdbcDriver
url=jdbc:hsqldb:mem:mydb
username=sa
password=

sqlMap 标签用来引用所有的映射文件,下面我们将详细介绍如何定义这些文件。

3.创建 iBATIS 映射文件

iBATIS 映射文件定义的是 SQL 和 JavaBean 的属性之间的映射关系,iBATIS 将根据这些映射文件,把从数据库中取得的对象填充到 JavaBean 一般属性或复杂属性中。

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

  • 定义简单映射关系
  • 定义缓存
  • 定义级联关系
  • 创建 StockType.xml –一般情形

StockType 是最简单的 JavaBean,没有复杂的映射关系,StockType.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.domain;

import com.ibm.etools.xsd.bean.runtime.AnyType;

public class StockType extends AnyType
{
 public StockType()
 {
 addElement("id", java.lang.String.class);
 addElement("stkTypeName", java.lang.String.class);
 }

 public StockType(String id, String name)
 {
 addElement("id", java.lang.String.class);
 addElement("stkTypeName", java.lang.String.class);
 this.basicSet("id", 0, id);
 this.basicSet("stkTypeName", 0, name); 
 }
 public String getId()
 {
 return (String)this.basicGet("id", 0);
 }
 public void setId(String id)
 {
 this.basicSet("id", 0, id);
 }
 public String getStkTypeName()
 {
 return (String)this.basicGet("stkTypeName", 0);
 }
 public void setStkTypeName(String stkTypeName)
 {
 this.basicSet("stkTypeName", 0, stkTypeName);
 }
}

请读者注意,此 Java 类的创建方法和以往不同的,采用的是第 4 小结中介绍的方法从 SDO 生成而来。读者可以在该小结中了解详细情况。

清单 -6:StockType.xml 定义

<?xml version="1.0" encoding="UTF-8" ?>
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
 "http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="StockType">

 <typeAlias alias="stockType" type="module.stock.implementation.domain.StockType"/>

 <cacheModel id="stockTypeCache" type="LRU">
 <flushInterval hours="24"/>
 <property name="size" value="100"/>
 </cacheModel>

<select id="getStockTypeList" resultClass="stockType" parameterClass="string" 
cacheModel="stockTypeCache">
	SELECT ST.ID as id, ST.TYPENAME as stkTypeName
	FROM STOCKTYPE ST
 </select>

<select id="getStockType" resultClass="stockType" parameterClass="string" 
cacheModel="stockTypeCache">
	SELECT ST.ID as id, ST.TYPENAME as stkTypeName
	FROM STOCKTYPE ST
	WHERE ST.ID=#value#
 </select>

 <insert id="insertStockType" parameterClass="stockType">
 INSERT INTO STOCKTYPE
 (ID, TYPENAME)
 VALUES
 (#id#, #stkTypeName#)
 </insert>

</sqlMap>
  • 属性说明:
    • typeAlias 属性:为带包名的全路径 Java 类指定简短的别名
    • cacheModel 属性:cacheModel 可以缓存 Mapped Statement 中得到的查询结果,使用“近期最少使用”策略,此例中,每 24 小时刷新一次。
    • select 属性:定义 Select 语句,这里需要注意的是,select 子句中的每一个字段的名字都要与 resultClass 中对应的 getter 方法名对应起来,如 id=“getStockTypeList”的 select 定义中,ST.ID 要给一个别名,即 id,以便于和 Java 类中的 getId() 对应起来。另外,由于数据库一般都不区分大小写,因此,要避免在 Java 类中定义类似 getFirstName() 和 getFirstname() 这种不同的方法,否则,数据库会把两者看作同一个属性,从而导致混乱。
    • parameterClass 可以指定复杂类,也可以是元数据类型,如 String。如果是 String,则在 select 语句中,可以通过 #value# 来引用。
    • insert 属性:定义 insert 语句。在此例中,参数类为 stockType,则在 insert 语句中,我们使用 stockType 中的对应的名称来获取他们的值,如 #id# 对应于 getId(),#stkTypeName# 对应于 getStkTypeName()。

有关 SQL Map 文件中各个属性更加翔实的介绍,请进一步阅读 iBATIS 解压包中 doc 目录下的 iBATIS-SqlMaps-2_en.pdf 文件。

StockHolder.xml 和 StockType.xml 文件的定义,和 Stock.xml 类似,这里不再赘述。下面列举一个比较复杂的例子。

  • 创建 StockAccount.xml –复杂情形

StockAccount 类比较复杂,它包含三个复杂类型的属性,即 stockHolder, stock,而 Stock 类又包含一个 stockType。在 iBATIS 中,我们不必太关心他们之间是一对一的关系还是多对一的关系。对于 getStockAccountListByStockId 业务,我们需要做的就是定义一个 select 语句把所有的字段都取到,然后由 iBATIS 完成 select 的输出到 stockHolder,stock 和 stockType 的转换。

清单 -7:Account 表定义

CREATE TABLE account(
 stockHolderId	INTEGER NOT NULL,
 stockId			VARCHAR(10) NOT NULL,
 balance			INT,
 profit			DECIMAL(17,3),
 constraint fk_account1 foreign key (stockHolderId)
 REFERENCES StockHolder (id),
 constraint fk_account2 FOREIGN KEY (stockId)
 REFERENCES stock (id)
);

清单 -8:StockAccount 类定义

package module.stock.implementation.domain;
import com.ibm.etools.xsd.bean.runtime.AnyType;
public class StockAccount extends AnyType
{
 public StockAccount()
 {
 addElement("stockHolder", StockHolder.class);
 addElement("stock", Stock.class);
 addElement("balance", java.lang.Integer.class);
 addElement("profit", java.lang.Double.class);
 addElement("stockMemo", StockMemo.class);
 }
 public StockHolder getStockHolder()
 {
 return (StockHolder)this.basicGet("stockHolder", 0);
 }
 public void setStockHolder(StockHolder stockHolder)
 {
 this.basicSet("stockHolder", 0, stockHolder);
 }
 public Stock getStock()
 {
 return (Stock)this.basicGet("stock", 0);
 }
 public void setStock(Stock stock)
 {
 this.basicSet("stock", 0, stock);
 }
 public int getBalance()
 {
 Integer result = (Integer)this.basicGet("balance", 0);
 return result == null ? 0 : result.intValue();
 }
 public void setBalance(int balance)
 {
 this.basicSet("balance", 0, new Integer(balance));
 }
 public double getProfit()
 {
 Double result = (Double)this.basicGet("profit", 0);
 return result == null ? 0.0D : result.doubleValue();
 }
 public void setProfit(double profit)
 {
 this.basicSet("profit", 0, new Double(profit));
 }
 public StockMemo getStockMemo()
 {
 return (StockMemo)this.basicGet("stockMemo", 0);
 }
 public void setStockMemo(StockMemo stockMemo)
 {
 this.basicSet("stockMemo", 0, stockMemo);
 }
}

清单 -9:StockAccount.xml 定义

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
 "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="StockAccount">
 <typeAlias alias="stockAccount" type="
 module.stock.implementation.domain.StockAccount"/>
 
 <select id="getStockAccountListByStockId" resultClass="
 stockAccount" parameterClass="string">
 
	SELECT A.STOCKHOLDERID AS "stockHolder.id" ,
	 SH.NAME AS "stockHolder.name" ,
	 SH.GENDER AS "stockHolder.gender" ,
		 SH.IDCARDNUM AS "stockHolder.idCardNum" ,
		 SH.EMAIL AS "stockHolder.email" ,
		 SH.ADDRESS AS "stockHolder.address" ,
		 A.STOCKID AS "stock.id" ,
 	 S.STKNAME	 AS "stock.stkName" ,
 	 S.STKTYPEID	 AS "stock.stkType.id" ,
 	 ST.TYPENAME AS "stock.stkType.stkTypeName", 
		 A.BALANCE,
		 A.PROFIT
	FROM ACCOUNT A, STOCKHOLDER SH, STOCK S, STOCKTYPE ST
	WHERE A.STOCKID=#stockId#
	AND A.STOCKHOLDERID=SH.ID
	AND A.STOCKID = S.ID
	AND A.STKTYPEID = ST.ID
 </select>

 <insert id="insertAccount" parameterClass="stockAccount">
	INSERT INTO ACCOUNT (STOCKHOLDERID, STOCKID, BALANCE, PROFIT)
	VALUES(#stockHolder.id#,#stock.id#,#balance#,#profit#) </insert>

</sqlMap>
  • 用返回字段的别名来指定对应 Java 类的属性:A.STOCKHOLDERID AS "stockHolder.id,即把 account 表的 STOCKHOLDERID 字段的值赋给 account 类的 stockHolder 属性的 id 属性,若用 Java 表述,就是 account.getStockHolder().setId(the value of A.STOCKHOLDERID)。对于没有别名的字段,就直接映射到 stockAccount 的简单类型的属性中。

4.创建 Java 类

使用 WebSphere Integration Developer,程序员能够以模型驱动的方式来创建类图和对应的 Java 类。方法如下:

以 Stock. 为例,在 Java 透视图下,右键点击 sdo/Stock.xsd,选择 Generate->Java…菜单,如下图所示:

图 -5:从 SDO 生成 Java class 所在菜单
图 -5:从 SDO 生成 Java class 所在菜单
图 -5:从 SDO 生成 Java class 所在菜单
图 -6:从 SDO 生成 Java class 向导
图 -6:从 SDO 生成 Java class 向导
图 -6:从 SDO 生成 Java class 向导

在“Generate Java”向导中,保留默认选项,点击“Finish”完成,则生成 Stock.java 和 StockType.java

清单 -10: Stock 类定义

package module.stock.implementation.domain;
import com.ibm.etools.xsd.bean.runtime.AnyType;
public class Stock extends AnyType
{
 public Stock()
 {
 addElement("id", java.lang.String.class);
 addElement("stkName", java.lang.String.class);
 addElement("stkType", StockType.class);
 }
 public String getId()
 {
 return (String)this.basicGet("id", 0);
 }

 public void setId(String id)
 {
 this.basicSet("id", 0, id);
 }
 public String getStkName()
 {
 return (String)this.basicGet("stkName", 0);
 }
 public void setStkName(String stkName)
 {
 this.basicSet("stkName", 0, stkName);
 }
 public StockType getStkType()
 {
 return (StockType)this.basicGet("stkType", 0);
 }
 public void setStkType(StockType stkType)
 {
 this.basicSet("stkType", 0, stkType);
 }
}

读者可以发现,WID 不仅创建了 Stock.java,还创建了 StockType.java。这种级联的 JavaBean 创建方式,加快了开发速度。

以此类推,创建以下 JavaBean

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

为方便统一管理代码,我们将默认路径 stockmodule.sdo 下的文件,拷贝到

gen/src/module/stock/implementation/domain 目录下,并将 stockmodule.sdo 目录及其中文件删除。

5.创建数据访问接口类

为了统一处理与数据库相关的操作,我们创建以下 Class 实现数据库交互:

清单 -11:StockPersistentService 类用途

类名 用途
StockPersistentService.java 封装所有的数据库操作,为 StockServiceImpl.java 提供数据持久化的统一接口。

我们用 Singleton 模式创建这个类。在其构造函数中,我们需要完成以下几件事:

1)为了与数据库交互,我们需要创建一个 SqlMapClient 的实例,通过调用此类的一些方法(如 insert(),queryForList() 和 queryForObject() 等)来实现我们需要的查询,插入等操作。

清单 -12:创建 SqlMapClient 实例

try {
Reader reader = 
Resources.getResourceAsReader("module/stock/implementation/service/sql-map-config.xml");
 // SqlMapClientBuilder 将读取 sql-map-config.xml 文件,并创建 SqlMapClient 的实例
sqlMapper = SqlMapClientBuilder.buildSqlMapClient(reader);
 reader.close(); 
} catch (IOException e) {
 throw new RuntimeException("Something bad happened while building the 
 SqlMapClient instance." + e, e);
}

2)由于我们采用的是 HSQL 内存数据库,因此在 StockPersistentService 创建时,需要运行几个 SQL 脚本用来创建数据表的 SQL 脚本和插入一些测试数据。

清单 -13:运行 SQL 脚本

…
Class.forName(driver).newInstance();
Connection conn = DriverManager.getConnection(url, username,password); 
// 使用 ScriptRunner 可以提交 SQL 脚本
ScriptRunner runner = new ScriptRunner(conn, false, false);
runner.setErrorLogWriter(null);
runner.setLogWriter(null);
runner.runScript(Resources.getResourceAsReader("
 module/stock/implementation/create_db_objects_hsql.sql"));
runner.runScript(Resources.getResourceAsReader("
 module/stock/implementation/init_db_objects_hsql.sql"));

StockPersistentService 剩下的部分就是使用 SqlMapClient 实现下列方法:

  • public String addStock(String stockId, String stockName, String stockTypeId)
  • public Stock getStock(String id)
  • public List getStockListByStockHolder(String stockHolderId)
  • public void addStockAccount(StockAccount sa)
  • public List getStockAccountListByStockId(String stockId)
  • public void addStockType(String name, String id)
  • public List getStockTypeList()

如:

清单 -14:public List getStockListByStockHolder(String stockHolderId) 方法定义

 public List getStockListByStockHolder(String stockHolderId){
	 	try {
 return sqlMapper.queryForList("getStockListByOwner",stockHolderId);
 					} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	 }

您可以看到,我们只需要指定在 SQL Map 文件中定义的脚本 id(如 getStockListByOwner),就可以得到符合条件的 Stock 列表。

6.实现 StockService 模块

StockServiceImp 是 StockService 模块的实现类,我们使用 iBATIS 充当数据访问服务层,方便的把数据库的数据通过 JavaBean 与 SDO 进行交换。

图 -7 描绘了 SDO,JavaBean 与数据库的交互过程:

图 -7:SDO – JavaBean - DB
图 -7:SDO – JavaBean - DB
图 -7:SDO – JavaBean - DB

我们的任务是实现 StockServiceImp 类中的所有方法,例如 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 中,然后再利用 iBATIS,把数据持久化到 DB 中,代码如清单 -15 所示:

清单 -15:addStockAccount 的实现代码

public String addStockAccount(DataObject stockHolder, DataObject stock,
			Integer account) {
		String message = null;
		if (stockHolder != null && stock != null) {
			try {
		DataObject stockAccountBO = boFactory.create("http://StockModule/sdo",
				"StockAccount");
                //将DataObject转换为Java 类
				StockHolder sh = convertToStockHolder(stockHolder);
				Stock st = convertToStock(stock);

				StockAccount sa = new StockAccount();
				sa.setStock(st);
				sa.setStockHolder(sh);
				sa.setBalance(account.intValue());
				sa.setProfit(0);
                //调用StockPersistentService完成数据持久化操作
			StockPersistentService.getInstance().addStockAccount(sa);
			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 转化:

清单 -16: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 类中。
  • 最后调用 iBATIS 的 Session.persiste 等方法将 StockAccount 数据存储到数据库中。

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

StockPersistentService.getInstance().addStockAccount(sa);

  1. getStockList 方法– DB 到 SDO 的转化更加简洁

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

清单 -17: getStockList(String idNum) 定义

public DataObject getStockList(String idNum) {
		DataObject stockListBO = null;
		DataObject stockBO = null;
		ArrayList stockList = new ArrayList();
		if (idNum != null) {
			try {
		//create the StockList sdo data object and set its properties
		stockListBO = boFactory.create("http://StockModule/sdo", "StockList");
		Iterator iter =
 StockPersistentService.getInstance().getStockListByStockHolder(idNum).iterator();
				while (iter.hasNext()) {
					Stock stock = (Stock) iter.next();
					stockBO = convertToStockBO(stock);
					stockList.add(stockBO);
				}
				stockListBO.setList("stocks", stockList);
			} catch (Exception e) {
				e.printStackTrace();
				stockListBO = null;
			}
		}
		return stockListBO;
	}
  • StockPersistentService.getInstance().getStockListByStockHolder(idNul) 方法直接调用

sqlMapper.queryForList("getStockListByOwner",stockHolderId) 方法来获取原本非常复杂的数据。这充分体现了 iBATIS 的优势。

注意:StockModule 的源代码目录默认是 StockModule 项目的根目录和 StockModule/gen/src,而默认的输出目录是 StockModule 根目录,因此,对于所有 StockModule/gen/src 目录以及子目录中的所有非 Java 资源,您必须手动将他们拷贝到默认的输出目录中,如 StockModule/module 目录下。

7.模块测试

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

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

总结

本文结合示例详细探讨了使用 iBATIS 构建 SCA Module 持久层的方法,并且在创建示例的过程中,使用了基于模型驱动的设计方法构建 SCA Module 和 Java 实现。使用 iBATIS 可以使程序员以非常自然的方式实现 SQL 语句和 Java 类之间的映射。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services, Java technology, WebSphere
ArticleID=316546
ArticleTitle=在 SCA Module 中使用 iBATIS 框架实现数据持久层
publish-date=06262008