DB2 pureXML 动态编程组合拳:iBatis+BeanUtils+JiBX

黄耀华, 软件工程师, IBM
李玉明 (ymli@cn.ibm.com), 软件工程师, Systems Documentation, Inc. (SDI)
袁飞 (feiyuan@cn.ibm.com), 软件工程师, Systems Documentation, Inc. (SDI)

简介: DB2 pureXML 为管理 XML 数据提供了丰富的功能,在数据存储层面对 XML 提供了强有力的支持,这毫无疑问给软件开发人员带来了灵活性。而一个完整的应用程序除了数据存储层之外,还有数据访问层,业务逻辑层,界面展示层等。因此,如果以 pureXML 技术为基础,并在各个层面提供相应的解决方案,那么整个应用程序的框架将是非常灵活的。

发布日期: 2008 年 9 月 02 日 
访问情况 : 3184 次浏览 
评论: 0 (查看 | 添加评论 - 登录)

平均分 0 星 共 0 个评分 平均分 (0个评分)
为本文评分

简介

本文旨在阐述一种基于 pureXML 技术的应用程序开发模式,借助 pureXML 的存储灵活性,iBatis 的数据访问适应性,BeanUtils 和 JiBX 的动态类生成,以及 XSLT 带来的界面展示灵活性,通过这些工具的组合达到整个软件框架的高度灵活,以期尽可能的适应需求带来的变化。

基于 DB2 pureXML 的应用程序开发模式

本文要介绍的开发模式主要针对需求易变的应用开发,使用各种工具和技术来增强框架的灵活性。 随着 XML 描述能力和灵活性被逐渐认识,许多 XML 相关技术应运而生,越来越多的应用使用了 XML 技术。一个基于 XML 的应用开发常常可以划分为几个层面:数据存储层、数据访问层、业务逻辑层和界面展示层。

DB2 pureXML 技术的逐渐成熟,为这些基于 XML 的应用开发提供了有力的支持。 pureXML 技术提供了高效的 XML 存储方案,同时支持标准的 XML Query 语言,使得应用程序能方便、有效地组织 XML 数据,在数据存储层提供了灵活性。而为了使 pureXML 技术更好的整合到整个应用开发中,需要考查其它层面上的支持技术。

在基于 DB2 pure XML 的应用开发中,在数据访问层使用 iBatis 将是非常合适的。iBatis 是一个开源的持久层框架,它支持 POJO 类和数据库表之间的映射,为应用的上层提供了数据访问的抽象。和其他 ORM 框架相比,iBatis 在基于 DB2 pure XML 的应用开发中有着自己的优势。首先,iBatis 提供了灵活的映射定义方式,开发人员可以通过使用 SQL/XQUERY 语句描述数据库数据的组织逻辑,随着 XML 数据的引入,这种映射定义与实现机制表现出了良好的适用性,而其他的 ORM 框架对 XQUERY 的支持不是很好。其次,由于 iBatis 允许在映射文件中制定 SQL/XQUERY 语句,给开发人员提供了较大的灵活性,可以使用高效的 SQL/XQUERY 语句对数据进行访问。

在业务逻辑层,使用 JiBX 和 BeanUtils,JiBX 为数据绑定提供了支持,DeanUtils 则为动态类提供了支持。

复杂的业务逻辑,首先需要完成 POJO 与 XML 文档的数据绑定。JiBX 是一个数据绑定框架,它能够很好的实现 XML 数据与 Java 对象之间的绑定,从而满足应用程序中二者之间相互装换(Marshal/Unmarshal)的需求。对比其它数据绑定框架,JiBX 使用字节码增强以及 XPP(XML Pull Parsing)技术,拥有出色的性能(参见 参考资源 [5])。在基于 DB2 pure XML 的应用程序开发中,针对较频繁的 XML 数据与 Java 对象转换,JiBX 是一种不错的选择。

由于在业务逻辑层使用 POJO 对应 XML 数据的表示,而 POJO 需要动态改变以适应变化。BeanUtils 工具支持动态的修改 Java Bean,可以实现动态 POJO,已适应适应一些数据结构的变化需求。使用 BeanUtils 提供的工具类另一个优势是可以提高开发效率,开发人员可以使用较少的代码方便地实现对 Bean 的一些操作。

在界面展示层,引入 XSLT 技术。XSLT 是一种基于 XML 文档转换语言,可以将 XML 按照定义好的样式表进行转换。XSLT 可以实现 XML 数据与其表现形式解耦,对于同一份 XML 数据,可以应用不同的样式表,从而达到不同的表现形式。针对显示层需求的变化,只需修改样式表就可以。这种 XML 数据与其表现形式之间的松耦合为 XML 应用程序的前端开发带来较高的可扩展性,提高了 XML 应用开发的灵活性。

上述各层的考查,为基于 DB2 pureXML 的应用程序开发提供了一个实用的模式。本文接下来将简要介绍开发中所需的工具技术,以期让读者更好的了解这种开发模式。

背景知识

pureXML

XML 具有自我描述性(易理解性)、灵活性、平台无关性和标准化等优点,这一优秀的技术广泛应用到各个领域。DB2 9 为 XML 提供了很好的数据存储和管理机制,IBM 将这一技术称之为“pureXML”。DB2 V9 对 XML 进行高效地原生态的存储,并支持 SQL 和 XQuery 两种查询方式。不仅如此,所有关系型的操作方式和数据库工具都可用于 XML 之上,例如索引机制、导入导出、关联查询、高速批量加载、系统优化等等。在 pureXML 特性的支持下,DB2 V9 成为一个既支持关系数据,又原生态支持 XML 的名副其实的双引擎数据库。有了对 XML 数据的强大支持,DB2 pureXML 在企业中的应用价值也在日益得到广泛关注。

iBatis

IBatis 是一个“半自动化”的 ORM 框架。这里的“半自动化”,是相对 Hibernate 等提供了全面的数据库封装机制的“全自动化”ORM 实现而言,程序员往往只需定义好了 POJO 到数据库表的映射关系,SQL 可以自动生成和执行,即通过 Hibernate 或者 OJB 提供的方法完成持久层操作。而 iBatis 的着力点,则在于 POJO 与 SQL 之间的映射关系。也就是说,iBatis 并不会为程序员在运行期自动生成 SQL/XQuery 执行,具体的 SQL/XQuery 需要程序员编写,然后通过映射配置文件,将 SQL/XQuery 所需的参数,以及返回的结果字段映射到指定 POJO。这种半自动化为开发者提供了一定的自由发挥的空间。

下面就对 iBatis 工作机制作一个简单介绍,读者可以进一步查阅本文提供的参考资料。

IBatis 设计模式的核心部分就是 SQL Map,它需要提供一个 XML 文件来取得数据库的连接及实现简单 Java bean 与 SQL 的映射声明。SQL Map 并不仅仅是一个 XML 文件,它其实一个设计模式。在执行映射声明时,SQL Map 将创建一个 JDBC 的 PreparedStatement 实例,用在 XML 中描述的对象作为 PreparedStatement 实例的参数,执行 PreparedStatement 并从 Result 中创建结果对象,这个执行流程用图 1 表示:


图 1。 iBatis 执行流程图 
iBatis 执行流程图 

JiBX

JiBX 是一款非常优秀的 XML 数据绑定框架,JiBX 的转换效率是目前很多开源项目都无法比拟的。它通过灵活的绑定映射文件来实现数据对象与 XML 文件之间的转换,这个文挡就是联系 XML 数据与 Java 对象之间的桥梁。

一个简单的绑定的例子如图 2 所示:


图 2。 一个简单的绑定定义 
一个简单的绑定定义 

在这个例子中,JiBX 在绑定文件中定义了 XML 的子元素和 Java 类成员的映射关系,从而将 XML 元素与相应的 Java 对象特性联系起来。JiBX 提供的绑定定义非常灵活,感兴趣的读者可以从参考资料中详细了解。

使用 JiBX 绑定 XML 文档与 Java 对象分为两步 :

第一步是编译绑定,也就是将您完成的绑定定义实际地编译成类文件。JiBX 提供了一个用于此目的的绑定编译器。可以直接运行编译器,将一个或多个绑定定义文件路径作为参数运行 org.jibx.binding.Compile 程序。比如:如果 JiBX 的安装路径在 C: 下,您就可以在绑定定义文件 binding.xml 所在的路径下运行下面的命令:

java –jar C:jibxjibx-bind.jar binding.xml

如果使用 Ant 编译工具完成编译工作 , 详细部分请参考 参考资源 [4]

第二步运行绑定,实现 XML 文件与 Java 实例之间的互相转换,使用 JiBX 运行时来对文档进行数据编组(marshal)和数据编出(unmarshal)。数据编组是由 Java 对象生成 XML 文挡的过程,而数据编出是根据 XML 文挡建立 Java 对象的过程。

BeanUtils

BeanUtils 是 Apache Jakarta Commons 项目的一个功能强大的组件。主要是对 Java 反射和自省 API 的包装,其目的是利用反射机制对 JavaBean 的属性进行处理。BeanUtils 的优势就是能够实现一个动态的 Bean,也就是不用事先定义一个标准的 JavaBean 类,而是根据业务需要随时动态地添加属性,并且可以作为一个 JavaBean 一样使用,即可以用 BeanUtils 操作其他 JavaBean 的方法一样进行操作。

BeanUtils 一共分 4 个包:

  • org.apache.commons.beanutils
  • org.apache.commons.beanutils.converters
  • org.apache.commons.beanutils.locale
  • org.apache.commons.beanutils.locale.converters

其中前两个是 BeanUtils 的默认实现,它没有针对本地化的任何处理,这个可以提高执行效率。但是若你的程序对于本地化有要求的话,那还是使用后面两个包比较安全。

下面介绍 BeanUtils 几种比较典型的方法:

(1)LazyDynaBean:它实现一个动态的 Bean,直接往里面加入属性,并作为一个 JavaBean 一样使用。例如:

/* 这里使用 LazyDynaMap,它是 LazyBean 的一个轻量级实现 */
LazyDynaMap dynaBean = new LazyDynaMap();
dynaBean.set("name", "Jone"); // set simple type
dynaBean.set("address", "stress", "HaiDian"); // set map type
dynaBean.set("phone", 0, "82502987"); // set index type
Map myMap = dynaBean.getMap(); // retrieve the Map

(2)属性拷贝:该方法可以直接进行 Bean 之间的克隆(clone),如:

BeanUtils.copyProperties(c1, c2);

但是这种 copy 都是浅拷贝,复制后的两个 Bean 可能拥有同一个对象的引用,这个在使用时要小心。

XSLT

XSLT 是一种将 XML 文档转化为其他形式的文档的语言,是 W3C 的推荐标准。提到 XSLT 不得不提及 XSL(eXtensible Stylesheet Language),即可扩展样式表语言,同样也是 W3C 的推荐标准。XSL 是 XML 的样式表,这一点类似于作为 HTML 的样式表 CSS,但是它又比 CSS 复杂得多,它实际上包含三种语言:转换 XML 的语言 XSLT、定义 XML 部分或模式的语言 XPath、定义 XML 显示方式的语言 --XSL 格式化对象。

XSLT 是 XSL 标准中最重要的部分,它用于将一个 XML 文档转换成另一个 XML 文档或另一种类型的文档。将 XML 转换成 HTML 是 XSLT 的主要功能之一。XSLT 可以向输出文件中增加全新的元素,或去掉一些元素。它可以重新安排这些元素并对元素进行分类,测试并确定显示哪些元素等等。

描述这种转换过程的一个常用说法是:XSL 用 XSLT 将一个 XML 来源树转换成另一个 XML 结果树(或将一个 XML 源文档转换成另一个 XML 结果文档),图 3 示表示了该流程:


图 3。 XSLT 输入和输出的树状结构 
XSLT 输入和输出的树状结构 

应用场景及解决方案

本文的前半部分对 pureXML、iBatis、JiBX、BeanUtils、XSLT 等技术作了较详细的介绍,那么在实际应用开发中如何借助 pureXML 的存储灵活性,iBatis 的数据访问适应性,BeanUtils 和 JiBX 的动态类生成,以及 XSLT 带来的界面展示灵活性,以达到整个软件框架的高度灵活,这是本文的重点所在。

下面就以一个具体的应用场景为例,在简单介绍该场景的业务流程之后,将给出详细的程序设计思路。

应用场景

本文的应用场景是一个资源交换平台,该平台主要为用户提供资源的上传和下载功能,用户也可以对已有的资源信息进行编辑,如添加一些描述信息、资源链接或者上传相关资源等。因为用户上传的资源种类繁杂,描述信息相当丰富,因此采用 XML 作为描述资源信息无疑是最好的选择。

该应用场景的业务流程框架图如图 4:


图 4。 业务流程框架图 
业务流程框架图 

如果用户新上传一种资源,这就需要对该资源添加新的描述信息,为了给用户足够的自由空间,平台以动态模型定制的方式让用户先定义信息录入的模板或者选择别人定义过的模板,然后依据模板再录入信息。这样做的好处是既把自由空间留给了用户,又能够使系统把握这些丰富的 XML 数据。

如果用户要编辑已有的资源描述信息,平台首先个性化展示已有的描述信息并接收用户添加的新信息,考虑到有些模块接口需要以 Java 对象的形式作为参数来处理这些描述信息,平台要能够提供编辑后的信息的 Java 对象,或者直接将其存入数据库。

解决方案

这个数据交换平台的最大的特点是处理的数据灵活多变,并且随着业务量的不断增长,数据也逐渐变得复杂,这使结构固化的 E-R 模型难以适应数据的复杂性、灵活性、层次性、以及个体差异,而由于 XML 具有自描述特性,可以很好地应对信息的复杂性、可理解性、灵活性以及可扩展性带来的挑战,因此采用 XML 作为信息的载体无可厚非。

DB2 V9.5 的 pureXML 提供了高效管理 XML 数据的功能,并为应用程序提供简单、方便的访问方法,程序员只要按照 W3C 制定的 XML/SQL 标准就可以轻松地操作 XML 数据,因此,选择 pureXML 作为后台数据库无疑给整个平台的运行打下坚实的基础;Batis 允许自由配置 XMLQUERY,这些 XMLQUERY 在某些情况下可以被复用,同时它能够将结果映射为 Java 对象,使用该框架可以有效简化应用开发;在需要个性化展示 XML 的地方考虑使用 XSLT 转换,因为每一个描述信息的 XML 都有相应的模板即 schema,所以 XSLT 可以根据 schema 的结构转化 XML 并个性化显示;当用户需要为已有描述信息添加新的描述时,可以利用 BeanUtils 将从界面上得到的若干新描述信息添加到由 JiBX 生成的动态 JavaBean 中,得到的 JavaBean 可以供其它的模块使用,或者利用 JiBX 的 marshall 功能将其映射成 XML 并存入数据库。


图 5。 系统架构图 
系统架构图 

方便起见,本文抽取“编辑描述信息”这一业务流程为案例,结合具体的程序设计使读者更清晰地认识该解决方案的灵活性。

首先,用户读取描述信息并进行编辑。这一步需要在 iBatis 的 sql Map 配置文件中配置相应的 sql 语句并执行,然后将得到的描述信息的 XML 用 XSLT 进行个性化展示。

1。配置 sql:


清单 1。 sql Map 在 iBatis 中的配置 
<select id="selectInfo" parameterClass="Description" resultClass="Description">
 select INFO from DISCRIPTION where ID=#id#
</select>

其中将参数和查询结果类型映射为事先定义的 Java Bean :Description,该 Java Bean 实现的是数据库表 DISCRIPTION 的字段到 Java 对象属性的简单映射,具体内容清单 2:


清单 2。 POJO 类 
/*mapping from table DISCRIPTION*/
public class Description {
	private int id;
	private String info;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getInfo() {
		return info;
	}
	public void setInfo(String info) {
		this.info = info;
	}
}

2。执行查询语句:


清单 3。 查询指定的描述信息 
import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
import com.ibm.db.Database;
public String selectInfoByID(Serializable id){
	try{
 reader=Resources.getResourceAsReader(resource);
	 SqlMapClient sqlMap=SqlMapClientBuilder.buildSqlMapClient(reader);
 Description param=new Description();
 Description result=new Description();
 param.setId((Integer)id).intValue());
 result=(Description)sqlMap.queryForObject("selectInfo", param);
 if(result!=null){
 return result.getInfo();
 }
 else
 return null;
	}
 catch(Exception e){
 e.printStackTrace();
 }
 return null; 
}

3。用 XSLT 作展示:

由于读取出来的 XML 文档是符合事先自定义的模板的(有关模板定制方面的技术,请参见 参考资源 [1]), 因此,只要按照事先定义的模板结构进行展示就可以了,当然,开发者可以根据自身的模型特点,编写不同的 XSLT 转换样式,使得展现的更加个性化,下面给出的是最通用的 XSLT 形式:


清单 4。 XSLT 个性化展示 
<?xml version="1.0" encoding="gb2312"?>
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <xsl:output method="html" encoding="gb2312" />
 <xsl:template match="/">
 <xsl:apply-templates select="/xsd:template/xsd:section">
 </xsl:apply-templates>
 </xsl:template>
 <xsl:template match="xsd:section">
 <table width="98%" border="0" cellpadding="0" cellspacing="1" class="xsltTable">
 <tr>
 <td width="100%" align="left">
 <b>
 <xsl:value-of select="@caption" />
 </b>
 <xsl:apply-templates select="./xsd:component">
 </xsl:apply-templates>
 </td>
 </tr>
 </table>
 <br></br>
 </xsl:template>
 <xsl:template match="xsd:component">
 <table width="100%" border="0" cellpadding="0">
 <tr>
 <td width="15%" align="left">
 <b>
 <xsl:value-of select="@caption" />
 </b>
 </td>
 <td width="85%" align="left">
 <b>
 <xsl:apply-templates select="./xsd:de">
 </xsl:apply-templates>
 </b>
 </td>
 </tr>
 </table>
 </xsl:template>
 <xsl:template match="xsd:de">
 <xsl:for-each select=".">
 <strong> <xsl:value-of select="@caption" /></strong>
 <font color="#00AAAA"> <xsl:value-of select="text()" /> </font>
 </xsl:for-each>
 <xsl:apply-templates select="xsd:de">
 </xsl:apply-templates>
 </xsl:template>
</xsl:stylesheet>

这时,用户就可以根据页面上的显示的描述信息,来对该描述信息进行编辑,假设他选择添加一些描述信息并保存,那么平台就可以利用 BeanUtils 将新添加的描述信息动态地添加到原来描述信息对应的 Java Bean 中,其前提是需要将原来的读取出来的 XML 文档转换成 Java Bean 的形式,这里就用到 JiBX 绑定框架,由于这里的 Java Bean 是动态生成的,为了实现这种动态的绑定,利用了 pureXML 来管理 JiBX 的绑定文件,即:每一个描述信息都对应一个绑定文件以便需要时在 XML 文档形式和 Java Bean 形式之间进行转换,同时为了保持这种映射关系,当其中任何一种形式发生了变化,都需要同步地更改相应的绑定文件并保存到数据库中。有了 pureXML 的支持,这种管理机制是很方便实现的,鉴于篇幅的原因本文就不再详细讨论了。

下面,利用 JiBX 将前面读取出来的描述信息 XML 文档转换为 Java Bean, 并利用 BeanUtils 将用户编辑的属性动态地添加到此 Java Bean 中以便其他模块使用。

4。用 JiBX 将 XML 转换为 Java Bean:

假设从数据库里读取的 XML 文档如清单 5:


清单 5。 查询数据库得到的 XML 文档 
<?xml version="1.0" encoding="UTF-8"?>
<Content>
 <Name>Gone With the Wind</Name>
 <Section>Eleven</Section>
 <Size>200MB</Size>
 <Description>It's so good!</Description>
</Content>

相应的 JiBX 绑定文件如清单 6:


清单 6 JiBX 绑定文档 
<binding>
 <mapping name="Content" class="Content">
 <value name="Name" field="name"/>
 <value name="Section" field="section"/>
 <value name="Size" field="size"/>
 <value name="Description" field="description"/>
 </mapping>
</binding>

这样就可以通过 JiBX 的 Unmarshal 功能来实现从 XML 到 Java Bean 的自动转换了,具体转换过程如清单 7:


清单 7。 将 XML 文档转换为 Java Bean 
public Content getClassFromXML(String info){
 try{
 InputStream xmlStream=new InputStream(info);
 IBindingFactory bfact=BindingDirectory.getFactory(Content.class);
 IUnmarshallingContext uctx= bfact.createUnmarshallingContext();
 /*unmarshal description infomation selected from database*/
 Object bean = uctx.unmarshalDocument(xmlStream, null);
 return (Content)bean;
 }
 catch (JiBXException e) {
 e.printStackTrace();
 return null;
 }
}

根据 JiBX 的映射机制,转换后的 Java Bean 应该具有清单 8 的形式:


清单 8。 JiBX 转换得到的 Java Bean 
public class Content {
 public String name;
 public String section;
 public String size;
 public String discription;
 public String getDiscription() {
 return discription;
 }
 public void setDiscription(String discription) {
 this.discription = discription;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public String getSection() {
 return section;
 }
 public void setSection(String section) {
 this.section = section;
 }
 public String getSize() {
 return size;
 }
 public void setSize(String size) {
 this.size = size;
 }	
 public void setSize(String size) {
 this.size = size;
 }	
}

当然,JiBX 的映射机制是非常灵活的,它不仅支持这种元素与属性的一对一的映射,还支持部分映射,并且映射本身也有很大的灵活性,开发人员可以根据实际需要进行设计。

5。动态添加属性:

将用户新添加的描述信息动态添加到生成的 Java Bean 中,则动态添加过程如清单 9:


清单 9。BeanUtils 动态添加属性 
public Content addAttribute (Content content,
 HashMap attributeNames,HashMap attributeValues)throws Exception{
 try{
 BeanUtils.setProperty(content,
 (String)attributeNames.get("URL"),attributeValues.get("URL"));
 BeanUtils.setProperty(content,
 (String)attributeNames.get("Role"),attributeValues.get("Role"));
 }catch(InvocationTargetException e){
 e.printStackTrace();
 }
 return content;
}

这时就得到了动态添加属性后新的 Java Bean,可以按照需求,利用 JiBX 的 Marshal 机制将其转换为 XML 文档存储在 pureXML 中,或者供其他模块使用。

总结

DB2 pureXML 的出现,为 XML 的存储和使用提供了很好的支持。本文着重介绍了 pureXML 如何与当前流行的工具和框架组合使用并给出一种开发模式。借助 pureXML 的存储灵活性,iBatis 的数据访问适应性,BeanUtils 和 JiBX 的动态类生成,以及 XSLT 带来的界面展示灵活性,达到了整个应用程序框架高度灵活的目的。


参考资料

作者简介

黄耀华,IBM 中国开发中心 DB2 for Linux Unix and Windows 的开发人员,具有丰富的开发经验,乐于分享丰富的数据库应用程序开发经验,尤其在 DB2 pureXML 方面有着较深刻的理解。他在 DB2 V9.7 中设计并开发了 Declared Global Temp Table 中对 XML 的支持。

李玉明是 IBM 中国软件开发中心的软件工程师。

袁飞是 IBM 中国软件开发中心的软件工程师。

原文地址:https://www.cnblogs.com/daichangya/p/12958771.html