一、为什么要设计“通用”的东西 二、基于什么技术
其中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: SELECT id, parentID $fieldList[]$ FROM $moduleTable$ WHERE id = #id# INSERT INTO $moduleTable$ (parentID $fieldValueList[].key$ ) VALUES (#parentID# #fieldValueList[].value# ) SELECT last_insert_id() UPDATE $moduleTable$ SET $fieldValueList[].key$ = #fieldValueList[].value# WHERE id = #id# DELETE FROM $moduleTable$ WHERE id = #id# 要注意的地方如下: a. 跟一般的ibatis配置文件不一样,该配置中没有包含resultMap,使用的就是resultClass的方式(效率没那么高的那种)。当然,也可以使用resultMap,这样就要为每个表写自己的配置文件了。因此,在该设计没完成前,我暂时先使用resultClass的方式。 b. 上面只列举了最简单的增删改以及按id查询,并没有更复杂的查询,为什么呢?因为我还在研究中。。。研究通用的模板sql的写法。 4. CustomPO对应的DAO: 我使用了ibaits提供的DAO框架,很好用,不单支持ibatis的框架,还支持Hibernate、JDBC等等,而且是与ibatis本身独立的,完全可以单独使用。以后就不用自己写DAO框架了。一下是该DAO接口: public interface ICustomDAO { /** * 通过传入moduleTable和id取得一条记录 */ CustomPO findByID(String moduleTable, int id) throws Exception; /** * 通过传入CustomPO对象取得一条记录 * @param po CustomPO 该对象在传入前应该先设置moduleTable和id参数, * 并且使用setFieldList()函数设置字段列表(该设置决定所返回的字段)。 */ CustomPO findByID(CustomPO po) throws Exception; /** * 通过传入moduleTable和parentID取得一条记录 */ CustomPO findByParentID(String moduleTable, int parentID) throws Exception; /** * 通过传入CustomPO对象插入一条记录 * @param po CustomPO 该对象在传入前应该先设置moduleTable和id参数, * 并且使用setFieldMap()函数设置“字段-值”对。 */ void insert(CustomPO po) throws Exception; /** * 通过传入CustomPO对象更新一条记录 * @param po CustomPO 该对象在传入前应该先设置moduleTable和id参数, * 并且使用setFieldMap()函数设置“字段-值”对。 */ void update(CustomPO po) throws Exception; /** * 删除moduleTable和id所对应的记录 */ void delete(String moduleTable, int id) throws Exception;} 我没有把所有的方法都列出来,反正挺简单的,跟一般的DAO没什么分别。 另外列几个实现的片断: a. 统一的数据装填函数,需要手工把id和parentID字段去掉。 protected void fill(Map result, CustomPO po) { Long returnId = (Long) result.get("id"); Long returnParentID = (Long) result.get("parentID"); result.remove("id"); result.remove("parentID"); if (returnId != null) po.setId(returnId.intValue()); if (returnParentID != null) po.setParentID(returnParentID.intValue()); po.setFieldMap(result); } b. 一般的查询,返回的是一个map,然后再用fill()函数 //查询 Map result = (Map)this.queryForObject("customPO_findByID", po); //处理返回结果 if(result == null) po = null; else fill(result, po); c. 增删改,没有返回值,值得一提的是增加操作完成后,po里面的id会更新,具体看前面相关的statement。 //增删改 this.insert("customPO_insert", po); this.update("customPO_update", po); this.delete("customPO_delete", po);
。。。 其中,name是字段名,column是字段对应数据表的字段名,type是字段类型,not-null是是否不能为空,unique是是否唯一。只有name这个属性是必填的,column如果不填默认与name相等,type默认为string,not-null和unique默认为false。 配置文件的读取类在这里就省略了。 为什么要自己定义一套这个框架?好像挺多此一举的,但是没办法,ibatis配置文件的信息是封闭的,我无法取得。另外我考虑的是: 四、有什么优点、缺陷 1. 优点: 2. 缺点: 五、后话
|