`
longgangbai
  • 浏览: 7308446 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

IBatis框架的一些问题的扩展(一)

阅读更多

          今年开发的项目XX项目中采用IBatis2.0+RCP +EJB3.0作为项目的架构,其中IBatis作为持久化框架,不采用Hibernate的原因很多,其中系统的高并发,高性能,大数据库量,消息通信类的系统,实时处理系统,偏向数据分析的系统等要求,同时由于hibernate技术的成败取决于开发人员的水平,由于人员水平参差不齐,无法保证对开源项目的实现掌控等原因,可能hibernate的有些逊色,故采用IBatis框架。


          不过以前的使用Hibernate的通常做法:针对项目中关系的性能要求低的采用Hibernate框架的常用功能,其他采用Hibernate native sql ,hql或者spring jdbcTemplate 基本上可以满足项目的基本要求。
  

           其中采用IBatis 的优点很多如IBatis框架简单易学,便于dba的测试,便于后期降低的维护成本,提高了开发的进度等。但是IBatis自身存在的问题然后需要改进。如IBatis自身移植性有限,考虑到以后可能数据的移植,进行相应的扩展方言的应用。

 

(注:以下代码是基于ibatis3 beta4的扩展,ibatis3正式版如果实现改变,将会继续跟进修改)

 

 

iBatis3默认使用的分页是基于游标的分页,而这种分页在不同的数据库上性能差异不一致,最好的办法当然是使用类似hibernate的基于方言(Dialect)的物理分页功能。

iBatis3现在提供插件功能,通过插件我们可以编写自己的拦截器来拦截iBatis3的主要执行方法来完成相关功能的扩展。

 

 

 

能够拦截的的类如下:

 

Java代码  收藏代码
  1. Executor  
  2.     (update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)  
  3. ParameterHandler  
  4.     (getParameterObject,setParameters)  
  5. ResultSetHandler  
  6.     (handleResultSets,handleOutputParameters)  
  7. StatementHandler  
  8.     (prepare,parameterize,batch,update,query)  

 

 但此次我们感兴趣的是Executor.query()方法,查询方法最终都是执行该方法。

该方法完整是:

 

Java代码  收藏代码
  1. Executor.query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;  

 

 

分页方言的基本实现:

以Mysql数据库示例,即有一个方法 query(sql,offset,limit);

 

 

Java代码  收藏代码
  1. query("select * from user",5,10);  

 

 

经过我们的拦截器拦截处理,变成

 

Java代码  收藏代码
  1. query("select * from user limit 5,10",0,0);  

 

 

1.拦截类实现:

 

Java代码  收藏代码
  1. @Intercepts({@Signature(  
  2.         type= Executor.class,  
  3.         method = "query",  
  4.         args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})  
  5. public class OffsetLimitInterceptor implements Interceptor{  
  6.     static int MAPPED_STATEMENT_INDEX = 0;  
  7.     static int PARAMETER_INDEX = 1;  
  8.     static int ROWBOUNDS_INDEX = 2;  
  9.     static int RESULT_HANDLER_INDEX = 3;  
  10.       
  11.     Dialect dialect;  
  12.       
  13.     public Object intercept(Invocation invocation) throws Throwable {  
  14.         processIntercept(invocation.getArgs());  
  15.         return invocation.proceed();  
  16.     }  
  17.   
  18.     void processIntercept(final Object[] queryArgs) {  
  19.         //queryArgs = query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)  
  20.         MappedStatement ms = (MappedStatement)queryArgs[MAPPED_STATEMENT_INDEX];  
  21.         Object parameter = queryArgs[PARAMETER_INDEX];  
  22.         final RowBounds rowBounds = (RowBounds)queryArgs[ROWBOUNDS_INDEX];  
  23.         int offset = rowBounds.getOffset();  
  24.         int limit = rowBounds.getLimit();  
  25.           
  26.         if(dialect.supportsLimit() && (offset != RowBounds.NO_ROW_OFFSET || limit != RowBounds.NO_ROW_LIMIT)) {  
  27.             BoundSql boundSql = ms.getBoundSql(parameter);  
  28.             String sql = boundSql.getSql().trim();  
  29.             if (dialect.supportsLimitOffset()) {  
  30.                 sql = dialect.getLimitString(sql, offset, limit);  
  31.                 offset = RowBounds.NO_ROW_OFFSET;  
  32.             } else {  
  33.                 sql = dialect.getLimitString(sql, 0, limit);  
  34.             }  
  35.             limit = RowBounds.NO_ROW_LIMIT;  
  36.               
  37.             queryArgs[ROWBOUNDS_INDEX] = new RowBounds(offset,limit);  
  38.             BoundSql newBoundSql = new BoundSql(sql, boundSql.getParameterMappings(), boundSql.getParameterObject());  
  39.             MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));  
  40.             queryArgs[MAPPED_STATEMENT_INDEX] = newMs;  
  41.         }  
  42.     }  
  43.       
  44.     private MappedStatement copyFromMappedStatement(MappedStatement ms,SqlSource newSqlSource) {  
  45.         Builder builder = new MappedStatement.Builder(ms.getConfiguration(),ms.getId(),newSqlSource,ms.getSqlCommandType());  
  46.         builder.resource(ms.getResource());  
  47.         builder.fetchSize(ms.getFetchSize());  
  48.         builder.statementType(ms.getStatementType());  
  49.         builder.keyGenerator(ms.getKeyGenerator());  
  50.         builder.keyProperty(ms.getKeyProperty());  
  51.         builder.timeout(ms.getTimeout());  
  52.         builder.parameterMap(ms.getParameterMap());  
  53.         builder.resultMaps(ms.getResultMaps());  
  54.         builder.cache(ms.getCache());  
  55.         MappedStatement newMs = builder.build();  
  56.         return newMs;  
  57.     }  
  58.   
  59.     public Object plugin(Object target) {  
  60.         return Plugin.wrap(target, this);  
  61.     }  
  62.   
  63.     public void setProperties(Properties properties) {  
  64.         String dialectClass = new PropertiesHelper(properties).getRequiredString("dialectClass");  
  65.         try {  
  66.             dialect = (Dialect)Class.forName(dialectClass).newInstance();  
  67.         } catch (Exception e) {  
  68.             throw new RuntimeException("cannot create dialect instance by dialectClass:"+dialectClass,e);  
  69.         }   
  70.         System.out.println(OffsetLimitInterceptor.class.getSimpleName()+".dialect="+dialectClass);  
  71.     }  
  72.       
  73.     public static class BoundSqlSqlSource implements SqlSource {  
  74.         BoundSql boundSql;  
  75.         public BoundSqlSqlSource(BoundSql boundSql) {  
  76.             this.boundSql = boundSql;  
  77.         }  
  78.         public BoundSql getBoundSql(Object parameterObject) {  
  79.             return boundSql;  
  80.         }  
  81.     }  
  82.       
  83. }  

 

 

2.ibatis3配置文件内容:

 

Xml代码  收藏代码
  1. <configuration>  
  2.     <plugins>  
  3.         <!-- 指定数据库分页方言Dialect, 其它方言:OracleDialect,SQLServerDialect,SybaseDialect,DB2Dialect,PostgreSQLDialect,MySQLDialect,DerbyDialect-->  
  4.         <plugin interceptor="cn.org.rapid_framework.ibatis3.plugin.OffsetLimitInterceptor">  
  5.             <property name="dialectClass" value="cn.org.rapid_framework.jdbc.dialect.MySQLDialect"/>  
  6.         </plugin>  
  7.     </plugins>  
  8.   
  9.     <environments default="development">  
  10.         <environment id="development">  
  11.             <transactionManager type="JDBC" />  
  12.             <dataSource type="POOLED">  
  13.                 <property name="driver" value="com.mysql.jdbc.Driver" />  
  14.                 <property name="url" value="jdbc:mysql://localhost:3306/test" />  
  15.                 <property name="username" value="root" />  
  16.                 <property name="password" value="123456" />  
  17.             </dataSource>  
  18.         </environment>  
  19.     </environments>  
  20.   
  21.     <mappers>  
  22.         <mapper resource="com/company/project/model/mapper/BlogMapper.xml" />  
  23.     </mappers>  
  24. </configuration>  

 

3.MysqlDialect实现

 

Java代码  收藏代码
  1. public class MySQLDialect extends Dialect{  
  2.   
  3.     public boolean supportsLimitOffset(){  
  4.         return true;  
  5.     }  
  6.       
  7.     public boolean supportsLimit() {     
  8.         return true;     
  9.     }    
  10.       
  11.     public String getLimitString(String sql, int offset, int limit) {  
  12.             return getLimitString(sql,offset,String.valueOf(offset),limit,String.valueOf(limit));  
  13.     }  
  14.       
  15.     public String getLimitString(String sql, int offset,String offsetPlaceholder, int limit, String limitPlaceholder) {  
  16.         if (offset > 0) {     
  17.             return sql + " limit "+offsetPlaceholder+","+limitPlaceholder;   
  18.         } else {     
  19.             return sql + " limit "+limitPlaceholder;  
  20.         }    
  21.     }     
  22.     
  23. }  

 

其它数据库的Dialect实现请查看:

http://rapid-framework.googlecode.com/svn/trunk/rapid-framework/src/rapid_framework_common/cn/org/rapid_framework/jdbc/dialect/

 

 

4.分页使用

直接调用SqlSession.selectList(statement, parameter, new RowBounds(offset,limit))即可使用物理分页

 

 

5.存在的问题

现不是使用占位符的方式(即不是“limit ?,?”而是使用“limit 8,20”)使用分页

 

完整代码可以查看即将发布的rapid-framework 3.0的ibatis3插件

 

           

备注:本项目中扩展方言的目的不再于数据库的移植性上,目的仅仅为分页服务,其他方面的暂时没有考虑方言的问题。

Ibatis中将Dialect注入到SqlExecutor中在数据库的数据操作中使用。

  定义方言的接口:

  package easyway.tbs.framework.app.pagination;

publicinterface  Dialect {

    /**

     * 是否支持分页

     */

    publicboolean supportsLimit();   

    /**

     * 拼装sql语句

     */

    public String getLimitString(String sql, int offset, int limit);

    publicboolean supportsLimitOffset();

} 

针对方言的接口的不同数据库的实现:

针对MySql版本:

package easyway.tbs.framework.app.pagination;

publicclass MySQLDialect implements Dialect {

publicboolean supportsLimitOffset() {

        returntrue;

    }

    publicboolean supportsLimit() {

        returntrue;

    }

    public String getLimitString(String sql, int offset, int limit) {

        if (offset > 0) {

            System.out.println(sql + " limit " + offset + "," + limit);

            return sql + " limit " + offset + "," + limit;
} else {

            System.out.println(sql + " limit " + limit);

            return sql + " limit " + limit;
}

    }

}

  针对Oracle

 package easyway.tbs.framework.app.pagination;

publicclass OracleDialect implements Dialect {

    publicboolean supportsLimit() {

        returntrue;

    }

    publicboolean supportsLimitOffset() {

        returntrue;

    }

    public String getLimitString(String sql, int offset, int limit) {

        sql = sql.trim();

        boolean isForUpdate = false;

        if (sql.toLowerCase().endsWith(" for update")) {

            sql = sql.substring(0, sql.length() - 11);

            isForUpdate = true;

        }

        StringBuffer pagingSelect = new StringBuffer(sql.length() + 100);

        if (offset > 0) {

    pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");

        } else {

            pagingSelect.append("select * from ( ");

        }

        pagingSelect.append(sql);

        if (offset > 0) {

            int end = offset + limit;

            pagingSelect.append(" ) row_ ) where rownum_ <= " + end

                    + " and rownum_ > " + offset);

        } else {

            pagingSelect.append(" ) where rownum <= " + limit);

        }

        if (isForUpdate) {

            pagingSelect.append(" for update");

        }

        System.out.println(pagingSelect.toString());

        return pagingSelect.toString();

    }

}

      项目建议:实现此方言的定义建议借鉴Spring和Hibernate中各种数据库的方言的封装(Hibernate移植性,可扩展性相对较好)Dialect主要注册各种数据库特有的函数和各种数据库特有的sql操作如数据库物理分页等。

 

 

分享到:
评论

相关推荐

    ssh2+ibatis框架

    **iBatis框架**:iBatis是另一种轻量级的ORM框架,它与Hibernate不同,不完全是一个全自动的解决方案。iBatis允许开发者编写自定义的SQL,提供更多的控制权。在SSH2框架中,iBatis可以与Spring集成,通过...

    ibatis框架简单应用

    Ibatis 是一个优秀的、开源的 Java 数据访问框架,它提供了 SQL 映射功能,可以将 SQL 语句与 Java 代码分离,从而实现更灵活的数据访问。Ibatis 主要解决的问题是简化 JDBC 的繁琐工作,通过 XML 或注解方式配置 ...

    SpringMvc+ibatis框架

    iBatis,另一方面,是一个SQL映射框架。它允许开发者编写自定义的SQL语句,将结果集映射到Java对象,避免了JDBC的繁琐操作。iBatis的核心是SqlMapConfig.xml配置文件,其中定义了数据源、事务管理器以及SQL映射文件...

    SpringMvc+Ibatis框架

    iBatis框架则提供了一种将SQL语句与Java代码分离的方式,通过XML配置文件或注解来定义SQL语句,使得数据库操作更易于维护和扩展。它的核心概念有SqlSessionFactory、SqlSession和Mapper。SqlSessionFactory创建...

    ibatis 框架原理实现

    在这个自己编写的Ibatis框架实现中,我们可以看到类似的思路和核心功能。 1. **XML配置文件**: XML文件是Ibatis框架的核心,它存储了SQL语句、参数映射以及结果集映射等信息。在`sqlmap`目录下,可能包含了多个...

    springmvc+ibatis 框架

    Spring MVC 作为Spring框架的一部分,是用于构建前端控制器(DispatcherServlet)的MVC设计模式实现,而iBatis则是一个轻量级的持久层框架,它提供SQL映射功能,简化了数据库操作。 1. **Spring MVC**: - **核心...

    struts2+spring+Ibatis框架包

    这个“struts2+spring+iBatis框架包”集成了这三个框架,使得开发者能够快速构建基于MVC(Model-View-Controller)模式的Web应用。 Struts2作为MVC框架,负责处理应用程序的控制逻辑。它通过Action类和配置文件定义...

    struts+spring+ibatis框架集合教程

    3. iBatis框架:iBatis是一个SQL映射框架,它将SQL语句与Java代码分离,提供了一种灵活的方式来执行数据库操作。通过XML或注解方式定义SQL语句,iBatis可以动态地生成和执行SQL,避免了传统的JDBC代码繁琐的模板式...

    spring struts ibatis 开发框架

    总的来说,Spring Struts iBatis框架组合提供了一个强大而灵活的开发环境,适合开发大型、复杂的企业级应用。通过熟练掌握这三个框架,开发者可以构建出符合现代开发标准的、易于维护的系统。在学习和使用过程中,...

    struts+spring+ibatis框架

    Struts框架提供了一系列的拦截器(Interceptor),使得开发者可以方便地添加业务逻辑和数据验证,同时,它还支持国际化和主题等功能,增强了应用的可扩展性和可维护性。 **Spring框架**则是一个全面的后端应用管理...

    iBATIS框架源码剖析-任钢

    《iBATIS框架源码剖析》这本书,作者任钢深入探讨了iBATIS的核心机制和实现原理,以下是对该框架的一些关键知识点的详细阐述。 1. **动态SQL**:iBATIS的一大亮点是它的动态SQL功能。通过在XML映射文件中使用条件...

    struts,ibatis框架

    iBatis框架: 1. **SQL映射**:iBatis的核心是XML或注解方式定义的SQL语句,将SQL与Java代码分离,增强了可读性和维护性。 2. **动态SQL**:iBatis支持动态SQL,允许在SQL语句中根据条件动态生成不同的查询,提高...

    传智播客ibatis教程_ibatis优点总结

    在J2EE开发中,经常将iBATIS与其他框架结合使用,如Struts(一个MVC框架)和Hibernate(一个ORM框架)。Struts处理用户的请求和响应,而iBATIS则作为数据访问层,负责与数据库的交互。Hibernate则可以用于更简单的...

    ibatis框架源代码

    通过对这些组件的源码阅读,我们可以学习到如何设计一个高效、可扩展的持久层框架,同时也可以借鉴Ibatis的优秀实践,比如它的动态SQL支持、缓存机制以及事务管理等。此外,源码中的异常处理、日志记录、单元测试等...

    ibatis框架实现的增删改查

    本篇文章将深入探讨如何使用Ibatis框架实现对`student`表的增删改查操作,包括模糊查询以及利用序列自动生成主键。 首先,我们来理解Ibatis的基本概念。Ibatis是由Apache基金会维护的一个开源项目,它是一个SQL映射...

    Ibatis .net框架多数据源配置

    Ibatis .net框架多数据源配置是针对在同一个应用程序中需要连接并操作多个数据库场景的一种解决方案。Ibatis是一个流行的持久层框架,它允许开发者将SQL语句与Java或.NET代码解耦,提供了一种灵活的方式来处理数据库...

    struts2+spring+ibatis框架实例

    "struts2+spring+ibatis框架实例"是基于这三个框架的一个集成示例,它利用MySQL作为后端数据库。下面我们将详细探讨这三个框架及其在实际项目中的应用。 **Struts2框架** Struts2是MVC(模型-视图-控制器)架构模式...

    springMVC+ibatis+hibernate+spring+boostrap框架

    iBatis 是一个 SQL 映射框架,它允许开发者将 SQL 查询直接写入 XML 文件或注解中,从而与 Java 代码解耦。这使得数据库查询更灵活,避免了传统的 JDBC 编程的繁琐。iBatis 可以与 ORM(对象关系映射)框架如 ...

Global site tag (gtag.js) - Google Analytics