相对Hibernate等“全自动”ORM机制而言,iBatis以SQL开发的工作量和数据库移植性上的让步,为系统设计提供了更大的自由空间。研究过iBaits以后,发现有些通用的方法可以解决企业级应用的常规工作,就是设计一个通用的持久层对象。
要注意的地方如下:
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); |
5、前面是通用的部分,光是通用是不够的。因此我另外建立了一套配置文件,记录字段对应关系。看看我所定义的一个配置文件,挺简单的:
<struct name="userInfo" table-name="tblUserInfo"> <field name="昵称" column="NICK_NAME" type="string" not-null="true" unique="false" /> <field name="姓名" column="FULL_NAME" type="string" not-null="true" unique="false" /> <field name="性别" column="SEX" type="string" not-null="false" unique="false" /> </struct> |
其中,name是字段名,column是字段对应数据表的字段名,type是字段类型,not-null是是否不能为空,unique是是否唯一。只有name这个属性是必填的,column如果不填默认与name相等,type默认为string,not-null和unique默认为false。
配置文件的读取类在这里就省略了。
为什么要自己定义一套这个框架?好像挺多此一举的,但是没办法,iBatis配置文件的信息是封闭的,我无法取得。另外我考虑的是:
a、viewer层
在web开发中,我可以在这套配置框架的基础上,建立自己的标签,实现数据自动绑定的功能;GUI开发中也可以做相应的功能。
b、module层
可以做通用的业务操作,不需要为每个业务都都做独立的业务逻辑。
四、有什么优点、缺陷
1、优点
a、“通用”,一切都是为了通用,为了减少重复劳动,一个个项目面对不同的业务,其实说到底都是那些操作。各种持久成框架已经带给我们不少的方便,但是在实际业务逻辑的处理上好像还没有一个这样的框架。
b、极大地减少代码量。前面说了,数据库改一个字段,PO、DAO、module、JO、viewer、validator全都要改一遍。使用了这套东西,可以把绝大部分的劳动都放在配置文件和UI上。当然,这是完美的设想,对于很多情况,业务逻辑还是要手工写的。
c、好像没有c了。
2、缺点
a、通常通用的东西都缺乏灵活性,在我的实际应用中也发现了这样那样的问题,解决方法都是以通用为基本原则。但是如果针对的是某个项目,那就可以针对业务来修改了。
b、性能问题。因为使用resultClass,按照文档所说的,性能没有resultMap好。当然也可以使用resultMap,如前所说,就要对每个PO写配置文件了,工作量也不少。
c、也好像没有c了。
五、后话
我总是喜欢写一些通用的东西,总是想把它设计成万能的。但是经过多次失败总结出来,我发现通用的东西在很多情况下都等于不能用。但我就是喜欢往通用方面想,这个毛病不知道什么时候才能改得了。其实在Delphi平台中,我早就实现了相关的东西,但是用delphi总是限制于ADO+Data Module这样掉牙的模式。现在转向java,发现有iBatis、hibernate这么多好的持久层框架,自然有移植必要了。
查看本文来源