站内搜索: 请输入搜索关键词

当前页面: 开发资料首页J2EE 专题基于iBatis的通用持久层对象

基于iBatis的通用持久层对象

摘要: 我总是喜欢写一些通用的东西,总是想把它设计成万能的。但是经过多次失败总结出来,我发现通用的东西在很多情况下都等于不能用。但我就是喜欢往通用方面想,这个毛病不知道什么时候才能改得了。其实在Delphi平台中,我早就实现了相关的东西,但是用delphi总是限制于ADO+Data Module这样掉牙的模式。现在转向java,发现有ibatis、hibernate这么多好的持久层框架,自然有移植必要了。
ibatis介绍
  使用ibatis 提供的ORM机制,对业务逻辑实现人员而言,面对的是纯粹的Java对象, 这一层与通过Hibernate 实现ORM 而言基本一致,而对于具体的数据操作,Hibernate 会自动生成SQL 语句,而ibatis 则要求开发者编写具体的SQL 语句。相对Hibernate等 “全自动”ORM机制而言,ibatis 以SQL开发的工作量和数据库移植性上的让步,为系统 设计提供了更大的自由空间。作为“全自动”ORM 实现的一种有益补充,ibatis 的出现显 得别具意义。

版权声明:任何获得Matrix授权的网站,转载时请务必保留以下作者信息和链接
作者:fellow99
原文:http://www.matrix.org.cn/resource/article/44/44410_iBatis.html
关键字:iBatis;ORM

一、为什么要设计“通用”的东西
  在大多数时候,我们所需要的持久层对象(PO)大多都是一张表(or视图)对应一个类。按照Hibernate的思想,就是抛开数据库的束缚,把焦点集中到业务对象中。而很多自动化工具的确让做到了通过表结构生成对应的对象,or通过对象自动生成表。对于小项目来说,一切都是简单的;对于有规范设计的项目来说,PO的设计也不是一件困难的工作。但是对于那些业务变动频繁的项目来说,改动PO可能成了一件很繁重的工作。试想一下,假设某个表需要增加一个字段:对于Hibernate(or iBaits),首先要改配置文件,然后PO,然后DAO(也许没有),然后业务逻辑,然后JO,然后界面,etc,贯通了全部层次。
  恩,写程序的都不喜欢这些重复劳动,但是做企业级应用的谁不是每天在这些工作中打滚。
  研究过iBaits以后,发现有些通用的方法可以解决,就是设计一个通用的持久层对象。

二、基于什么技术
  iBatis可以使用Map对象作为PO,Hibernate好像也有相关的功能(我没有细看,不确定)。
  iBatis执行一条指令的过程大概是这样的:



其中圈圈1、2、3描述了iBatis最重要的三个对象。

圈圈1:statement简单来说就是存储sql语句的配置信息,一个最简单的statement:


insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (1, “Shih Tzu”)


其中id属性是这个statement的唯一标识,全局不能重复。

以上当然是最简单的了,没有参数也不需要返回值,但实际情况下基本都需要传入参数,下面就是介绍参数。

圈圈2:参数对象主要分两种类型:parameterMap、parameterClass和Inline Parameter。

  其中parameterMap是配置文件定义传入参数表,如下:





insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (?,?);


  而parameterClass是传入参数对象(JavaBean),如下:


insert into PRODUCT values (#id#, #description#, #price#)


  Inline Parameter则是强化版的parameterClass,如下:


insert into PRODUCT (PRD_ID, PRD_DESCRIPTION)
values (#id:NUMERIC:-999999#, #description:VARCHAR:NO_ENTRY#);


  其中第一种方法看着就复杂,实际是为了兼容老版本留下来的,所以parameterClass是我们最常用的方法。官方文档对parameterClass介绍很详细,因为这是核心之一,具体请自己查阅。有3个特性说明一下:

  a. parameterClass对象可以传入一个Map对象(or Map子类)。本来如果是传入JavaBean,程序会通过get/set来分析取得参数;而Map是key-value结构的,那程序会直接通过key来分析取参数。

  b. 看以下语句:


insert into PRODUCT values (#id#, #description#, #price#, #classify.id#)


  蓝色部分#classify.id#翻译过来实际是product.getClassify().getId(),classify是Product对象的一个子对象。

  c. 在模板sql语句中除了“#”以外,还有“$”,它们两代表的意思当然不同了:


select * from PRODUCT order by $preferredOrder$


  “#”在生成sql语句的过程中,会变成“?”,同时在参数表中增加一个参数;

  “$”则会直接替换成参数对象对应的值,例如上面的preferredOrder的值可能是“price”,则生成的sql语句就是:select * from PRODUCT order by price。

  *需要特别说明的是传入参数这一部分将会是后面正题“通用持久层对象”的核心,怎么个通用法,怎么设计模板sql语句,都是在这部分上。



圈圈3:结果对象跟参数对象差不多,也有两种,resultMap和resultClass,如下:

  resultMap就是配置文件中预定义了要取得的字段:






select * from PRODUCT


  resultClass则是通过分析返回的字段,来填充结果对象:


SELECT PER_ID as id, PER_FIRST_NAME as firstName
FROM PERSON WHERE PER_ID = #value#


  跟参数对象相反,结果对象一般使用resultMap形式。引用官方的话:使用resultClass的自动映射存在一些限制,无法指定输出字段的数据类型(如果需要的话),无法自动装入相关的数据(复杂属性),并且因为需要ResultSetMetaData的信息,会对性能有轻微的不利影响。但使用resultMap,这些限制都可以很容易解决。


三、正题来了,怎么做“通用持久层对象”

1. 表结构:

  每个表都必须包含两个字段:id和parentId,其他字段按照需求来定义,其他各种索引、约束、关系之类的也按需求定义。

2. 通用的持久层对象,CustomPO:

public class CustomPO {
protected String moduleTable; //该PO对应的表名(视图名)
protected int id; //表的id
protected int parentID; //父表的id(如果有的话)
protected Map fieldMap; //字段Map,核心,用于存储字段及其值
public String getModuleTable()
public void setModuleTable(String moduleTable)
public int getId()
public void setId(int id)
public int getParentID()
public void setParentID(int parentID)
public Map getFieldMap()
public void setFieldMap(Map fieldMap)
public void copyFieldMap(Map fieldMap)
//取得字段名列表
public List getFieldList()
//设置字段名列表。如果fieldMap没有相应的字段,则增加,字段值为null;如果有则不增加。
public void setFieldList(List fieldList)
//返回字段的“字段名 - 字段值”列表,使用com.fellow.pub.util.KeyValuePair对象作为存储
public List getFieldValueList()
}

  那些成员变量的get/set就没什么说的,主要说说getFieldValueList()这个方法。该方法返回一个列表,列表元素是一个key-value结构,简单来说就是把字段map序列化。在构造模板sql语句时会体现它的用途。

3. iBatis对象配置文件CustomPO.xml: