Mybatis3.0出来已有段时间了,其实自己挺喜欢这样的一个持久化框架的,因为它简单实用,学习成本低。 Mybatis3.0在整体结构上和 ibatis2.X差不多,改进特性如下:
1.解析 xml引进了 Xpath,不像 ibatis2.x那样业余
2.动态 sql用 OGNL解析
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 })})
public class PaginationInterceptor implements Interceptor{
private final static 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 ){
throw new 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
public void 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 >
转自:http://www.cnblogs.com/jcli/archive/2011/08/09/2132222.html
运维网声明
1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网 享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com