`
longgangbai
  • 浏览: 7340629 次
  • 性别: 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映射...

    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