zoukankan      html  css  js  c++  java
  • mybatis高级应用系列一:分页功能

    Mybatis分页插件请使用这位帅哥开发的, 看起来不错. https://github.com/miemiedev/mybatis-paginator

    Mybatis3.0出来已有段时间了,其实自己挺喜欢这样的一个持久化框架的,因为它简单实用,学习成本低。Mybatis3.0在整体结构上和ibatis2.X差不多,改进特性如下:

    1.         解析xml引进了Xpath,不像ibatis2.x那样业余

    2.         动态sqlOGNL解析

    3.         加入注解配置sql,感觉没什么特别大的用途,我更喜欢xml方式,代码和配置分离,这也是ibatis的初衷

    4.         加强了缓存这块的功能。Mybatis3.0把缓存模块分得更细,分为“持久实现(prepetual)”和“资源回收策略实现(eviction)”,更好的对缓存功能进行自己组合和扩展

    5.         终于加入的plugin功能,就像struts一样,这样就可以很好的扩展内部的Executor,StatementHandler….等内部对象功能。

     

    一下只能想到这些了,总之改动后的代码结构清晰多了,如果各位看下源码的话,也是学习设计模式很好的课件,里面的代码用到了很多经典的设计模式,这在之后的系列学习中会讲到。

     

    这一篇文章讲下分页的功能。

     

    正如和ibatis以前的版本一样,mybatis的分页还是基于内存分页(查找出所有记录再取出偏移量的记录,如果jdbc驱支持absolute定位或者rs.next()到指定偏移位置),其实这样的分页实现基本没用,特别是大量数据情况下。

     

    要想改变mybatis内部的分页行为,理论上只要把最终要执行的sql转变成对应的分页语句就行了。首先,我们熟悉下mybatis内部执行查询的动态交互图:

     

    可以很清楚的看到,真正生成Statement并执行sql的语句是StatementHandler接口的某个实现,这样就可以写个插件对StatementHandler的行为进行拦截。  

    package study.mybatis.interceptor;





    import java.sql.Connection;

    import java.util.Properties;



    import org.apache.commons.logging.Log;

    import org.apache.commons.logging.LogFactory;

    import org.apache.ibatis.executor.statement.StatementHandler;

    import org.apache.ibatis.mapping.BoundSql;

    import org.apache.ibatis.plugin.Interceptor;

    import org.apache.ibatis.plugin.Intercepts;

    import org.apache.ibatis.plugin.Invocation;

    import org.apache.ibatis.plugin.Plugin;

    import org.apache.ibatis.plugin.Signature;

    import org.apache.ibatis.reflection.MetaObject;

    import org.apache.ibatis.session.Configuration;

    import org.apache.ibatis.session.RowBounds;



    import study.mybatis.dialect.Dialect;

    import study.mybatis.dialect.MySql5Dialect;



    @Intercepts({@Signature(type
    =StatementHandler.class,method="prepare",args={Connection.class})})

    publicclass PaginationInterceptor implements Interceptor{



    privatefinalstatic Log log = LogFactory.getLog(PaginationInterceptor.class);



    @Override

    public Object intercept(Invocation invocation) throws Throwable {

    StatementHandler statementHandler
    = (StatementHandler)invocation.getTarget();

    BoundSql boundSql
    = statementHandler.getBoundSql();

    MetaObject metaStatementHandler
    = MetaObject.forObject(statementHandler);

    RowBounds rowBounds
    = (RowBounds)metaStatementHandler.getValue("delegate.rowBounds");

    if(rowBounds ==null|| rowBounds == RowBounds.DEFAULT){

    return invocation.proceed();

    }

    Configuration configuration
    = (Configuration)metaStatementHandler.getValue("delegate.configuration");

    Dialect.Type databaseType
    =null;

    try{

    databaseType
    = Dialect.Type.valueOf(configuration.getVariables().getProperty("dialect").toUpperCase());

    }
    catch(Exception e){

    //ignore

    }

    if(databaseType ==null){

    thrownew RuntimeException("the value of the dialect property in configuration.xml is not defined : "+ configuration.getVariables().getProperty("dialect"));

    }

    Dialect dialect
    =null;

    switch(databaseType){

    case MYSQL:

    dialect
    =new MySql5Dialect();



    }



    String originalSql
    = (String)metaStatementHandler.getValue("delegate.boundSql.sql");

    metaStatementHandler.setValue(
    "delegate.boundSql.sql", dialect.getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit()) );

    metaStatementHandler.setValue(
    "delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET );

    metaStatementHandler.setValue(
    "delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT );

    if(log.isDebugEnabled()){

    log.debug(
    "生成分页SQL : "+ boundSql.getSql());

    }

    return invocation.proceed();

    }



    @Override

    public Object plugin(Object target) {

    return Plugin.wrap(target, this);

    }



    @Override

    publicvoid setProperties(Properties properties) {

    }



    }

    里面最重要的三条语句:

    metaStatementHandler.setValue("delegate.boundSql.sql", dialect.getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit()) );

    metaStatementHandler.setValue(
    "delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET );

    metaStatementHandler.setValue(
    "delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT );                                          
    改别要执行的sql语句,现在新设置的sql语句是物理分页的,所以现在不再需要mybatis进行额外的操作了,所以把rowBounds的偏移量恢复为初始值(offet:0,limit:Integer.max) 为了指定数据库版本,在mybatis全局配置文件设置dialect值
    <properties>

    <property name="dialect" value="mysql"/>

    </properties>

    <plugins>

    <plugin interceptor="study.mybatis.interceptor.PaginationInterceptor">

    </plugin>

    </plugins>

    完整代码请用svn从下面链接检出查看:

    svn checkout http://20110311start.googlecode.com/svn/trunk/

    下个系列将会讲下缓存的扩展应用。    
    -----------------------------分隔线---------------------------------------------

    最近有朋友用mybatis和spring整合的时候如果按照下列方式发现dialect属性不能设置成功:

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="typeAliasesPackage" value="com.***.web.domain" />
        <property name="plugins">
            <array>
                <ref bean="paginationInterceptor"/>
            </array>
        </property>
        <property name="configurationProperties">
            <props>
                <prop key="dialect">mysql</prop>
            </props>
        </property>
    </bean>

    这个问题是org.mybatis.spring.SqlSessionFactoryBean这个代码里有个bug(244行,或者不是bug,是作者不想这么做法),如果感兴趣可以看下源码。配置文件做下如下修改:

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="typeAliasesPackage" value="com.***.web.domain" />
        <property name="plugins">
            <array>
                <ref bean="paginationInterceptor"/>
            </array>
        </property>
        <!-- 这里不要,注释掉
        <property name="configurationProperties">
            <props>
                <prop key="dialect">mysql</prop>
            </props>
        </property>
        -->
        <!--  加上这个属性 -->
        <property name="configLocation" value="classpath:Mybatis_Configuration.xml"/>
    </bean>

    Mybatis_Configuration.xml的配置如下:

     1 <?xml version="1.0" encoding="UTF-8" ?>
    2 <!DOCTYPE configuration
    3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    4 "http://mybatis.org/dtd/mybatis-3-config.dtd">
    5 <configuration>
    6
    7   <properties>
    8     <property name="dialect" value="oracle"/>
    9   </properties>
    10
    11 </configuration>
  • 相关阅读:
    关于C++中操作符重载的疑问 :四个运算符=, ->, [], ()不可以重载为全局函数(友员函数)...
    linux内核移植过程问题总结
    关于开发板用tftp下载失败分析
    阿里云ECS下安装的MySQL无法远程连接?
    uva729
    使用 Confluence 6 服务器移动应用
    Confluence 6 移动浏览查看任务
    Confluence 6 移动浏览查看通知
    Confluence 6 移动浏览查看页面,博客和评论
    Confluence 6 移动浏览搜索内容和人
  • 原文地址:https://www.cnblogs.com/jcli/p/2132222.html
Copyright © 2011-2022 走看看