`
sbpya
  • 浏览: 610048 次
  • 性别: Icon_minigender_1
  • 来自: 杭州,长沙
社区版块
存档分类
最新评论

基于iBatis的通用持久层对象

阅读更多

 

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

二、基于什么技术


    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:

              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);


5. 前面是通用的部分,光是通用是不够的。因此我另外建立了一套配置文件,记录字段对应关系。看看我所定义的一个配置文件,挺简单的:

                。。。

  其中,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这么多好的持久层框架,自然有移植必要了。

 

分享到:
评论

相关推荐

    封装通用的Spring3+Struts2+IBatis的CRUD

    **IBatis** 是一个轻量级的持久层框架,它提供了一种SQL映射机制,允许开发者将SQL语句直接写在XML配置文件中,从而解耦了业务逻辑代码和数据库访问代码。在Spring3+Struts2的架构中,IBatis作为数据访问层,负责...

    ibatis的3个jar包.rar

    3. **ibatis-dao-2.jar**:DAO(Data Access Object)模式是一种常见的数据访问设计模式,Ibatis的DAO库提供了基于Ibatis的DAO实现,方便开发者创建数据库访问层的对象。它提供了一些基础的抽象,使得开发者可以更...

    ibatis jar文件

    这些JAR文件在Java项目中作为依赖使用,可以构建一个基于iBATIS或MyBatis的数据访问层,同时利用如Log4j进行日志记录,以及通过CGlib进行动态代理和对象创建。每个组件都有其特定的作用,共同构成了一个完整的数据库...

    ibatis 入门

    标题 "ibatis 入门" 暗示我们要探讨的是关于使用和理解开源持久层框架 iBATIS 的基础知识。iBATIS 是一个优秀的 Java 应用框架,它将 SQL 查询与 Java 代码分离,提供了更灵活的数据访问方式。这篇博客(博文链接已...

    ibatis与spring整合

    ibatis是一个开源的、基于Java的持久层框架,它提供了SQL映射的方式来进行数据库访问。与Hibernate等其他ORM框架相比,ibatis更加轻量级,对于那些只需要简单CRUD(创建、读取、更新、删除)操作的应用来说非常合适...

    Java EE数据持久层解决方案的设计与实现.pdf

    本篇论文深入探讨了这一领域,并提出了一种基于XML的通用持久层解决方案。 首先,数据持久化是Java EE应用开发的重要组成部分,尤其是在构建企业级应用系统时。它涉及到如何将业务对象的状态持久化到数据库中,以及...

    ibatis mybatis crud 完整代码

    在IT行业中,`iBatis` 和 `MyBatis` 是两种非常流行的持久层框架,它们主要用于简化Java应用程序中的数据库操作。本篇文章将深入探讨这两个框架,并基于`CRUD`(Create, Read, Update, Delete)操作来阐述如何使用它们...

    ibatis-sqlmap_2.3.4_2.jar两个版本的jar包

    Ibatis-SqlMap是早期非常流行的一款Java持久层框架,由Cody Aravena创建,后被Apache基金会接管并更名为MyBatis。本篇将详细阐述Ibatis-SqlMap 2.3.4和2两个版本的主要知识点。 一、Ibatis-SqlMap简介 Ibatis-...

    Ibatis+Spring+struts1框架搭建

    Ibatis是一个优秀的持久层框架,Spring是一个全面的后端应用框架,而Struts1则是一个经典的MVC(Model-View-Controller)架构框架。这三者结合可以构建出稳定且功能丰富的Web应用程序。 首先,让我们深入了解一下这...

    Struts+Spring+ibatis

    iBatis 是一个持久层框架,它允许开发者编写SQL语句并与Java对象进行映射。相比于传统的JDBC,iBatis 提供了更灵活的数据库操作方式,降低了SQL与Java代码的耦合度。iBatis与Spring整合后,可以使用Spring的DAO支持...

    struts-2.0.11+spring-2.5+ibatis-2.3及其它各种jar包

    iBatis 是一款持久层框架,它将 SQL 查询与 Java 代码解耦,使得开发者可以更专注于 SQL 而不是数据库操作。版本 2.3 提供了 SQL 映射文件和接口绑定,允许开发者直接执行预编译的 SQL 语句,提高性能和可维护性。 ...

    iBATIS开发指南.pdf

    iBATIS是一个流行的持久层框架,它通过XML或注解的方式将对象与数据库进行映射,避免了几乎所有的SQL代码和手动设置参数以及获取结果集。iBATIS相对于其它的ORM(对象关系映射)框架来说,提供了更多的控制,尤其在...

    Struts+srping+ibatis

    而iBatis作为一个持久层框架,简化了数据库操作,将SQL与Java代码分离。 Struts框架的核心是ActionServlet,它接收HTTP请求,解析请求参数,并根据配置文件(struts-config.xml)将请求转发给相应的Action。Action...

    ibatis+spring

    ibatis,现在更常被称为MyBatis,是一个支持普通SQL查询、存储过程以及高级映射的优秀持久层框架。ibatis消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。ibatis可以使用简单的XML或注解进行配置和原始...

    图书管理系统 ibatis extjs spring struts2

    iBatis 是一个优秀的持久层框架,它简化了Java应用程序与数据库之间的数据交互。在本图书管理系统中,iBatis 作为ORM(对象关系映射)工具,负责SQL语句的执行和结果映射,使得开发者无需直接编写大量的JDBC代码,...

    spring-ibatis

    而 iBATIS 是一个优秀的持久层框架,简化了数据库操作与业务逻辑的集成。将 Spring 与 iBATIS 集成,可以构建出高效、可维护的 MVC 应用程序。 在"spring-ibatis"项目中,我们可以看到主要关注的是 Spring MVC 与 ...

    ibatis_sql_map教程

    ibatis SQL Maps是一款开源持久层框架,它允许开发者通过XML配置文件来实现数据库操作与业务逻辑的解耦。对于那些希望快速上手ibatis SQL Maps的初学者来说,这份教程将会非常有帮助。 #### 二、准备工作 在正式...

    ibatis与memchaced使用说明

    ibatis是一个基于Java的持久层框架,它简化了Java应用程序与数据库之间的交互过程。ibatis的核心功能之一就是提供了缓存机制,尽管缓存并不是ibatis框架的主要特色,但是合理地理解和运用ibatis的缓存机制对于提升...

    iBatisTest测试Demo

    iBatisTest测试Demo是一个基于Java的项目,主要目的是演示如何使用iBatis这个轻量级的持久层框架进行数据库操作。iBatis是MyBatis的前身,它提供了一个SQL映射框架,允许开发者将SQL语句直接写在XML配置文件中,实现...

    Spring3.0+Ibatis+Oracle全注解开发详细过程

    3. **@Repository** - 此注解用于持久层,即数据访问对象(DAO)。它封装了与数据库交互的代码,如SQL查询,事务管理等。 4. **@Component** - 这是一个通用注解,适用于无法明确分类为@Controller、@Service或@...

Global site tag (gtag.js) - Google Analytics