虎门北栅那家做头发好镇北柵爱华网吧和曙光网吧可以用支付宝花呗吗?

MyBatis 通用分页 - 代码改变世界,积累成就自己 - ITeye技术网站
博客分类:
分页分为真分页和假分页,而
本身没有提供基于数据库方言的分页功能,而是基于
的游标分页,很容易出现性能问题。网上提供的一个解决方案感觉还不错,是基于
本身的插件机制,通过拦截
首先,我们需要根据不同数据库来加载不同的分页
,这里我们参考
,定义一个数据库方言接口
Dialect.java
package com.iflytek.mybatis.page.
* @author xdwang
下午7:45:24
* @description 数据库方言接口
public interface Dialect {
public static enum Type {
public String getValue() {
return "mysql";
public String getValue() {
return "sqlserver";
public String getValue() {
return "oracle";
* @descrption 获取分页SQL
* @author xdwang
* @create 下午7:48:44
* @param sql
原始查询SQL
* @param offset
开始记录索引(从零开始)
* @param limit
每页记录大小
* @return 返回数据库相关的分页SQL语句
public abstract String getPageSql(String sql, int offset, int limit);
然后分别定义不同类型数据库的具体分页,这里我们列举3个比较常用的,MySQL、MSSQL、Oracle
MySql5Dialect.java
package com.iflytek.mybatis.page.
* @author xdwang
下午7:50:44
* @description MySQL数据库实现
public class MySql5Dialect implements Dialect {
protected static final String SQL_END_DELIMITER = ";";
public String getPageSql(String sql, boolean hasOffset) {
return MySql5PageHepler.getPageSql(sql, -1, -1);
public String getPageSql(String sql, int offset, int limit) {
return MySql5PageHepler.getPageSql(sql, offset, limit);
public boolean supportsLimit() {
MySql5PageHepler.java
package com.iflytek.mybatis.page.
import java.util.regex.M
import java.util.regex.P
* @author xdwang
下午8:41:21
* @description MySql辅助方法
public class MySql5PageHepler {
* @descrption 得到查询总数的sql
* @author xdwang
* @create 下午8:41:10
* @param querySelect
public static String getCountString(String querySelect) {
querySelect = getLineSql(querySelect);
int orderIndex = getLastOrderInsertPoint(querySelect);
int formIndex = getAfterFormInsertPoint(querySelect);
String select = querySelect.substring(0, formIndex);
// 如果SELECT 中包含 DISTINCT 只能在外层包含COUNT
if (select.toLowerCase().indexOf("select distinct") != -1 || querySelect.toLowerCase().indexOf("group by") != -1) {
return new StringBuffer(querySelect.length()).append("select count(1) count from (").append(querySelect.substring(0, orderIndex)).append(" ) t").toString();
return new StringBuffer(querySelect.length()).append("select count(1) count ").append(querySelect.substring(formIndex, orderIndex)).toString();
* 得到最后一个Order By的插入点位置
* @return 返回最后一个Order By插入点的位置
private static int getLastOrderInsertPoint(String querySelect) {
int orderIndex = querySelect.toLowerCase().lastIndexOf("order by");
if (orderIndex == -1 || !isBracketCanPartnership(querySelect.substring(orderIndex, querySelect.length()))) {
throw new RuntimeException("My SQL 分页必须要有Order by 语句!");
return orderI
* 得到分页的SQL
* @param offset
* @param limit
* @return 分页SQL
public static String getPageSql(String querySelect, int offset, int limit) {
querySelect = getLineSql(querySelect);
String sql = querySelect.replaceAll("[^\\s,]+\\.", "") + " limit " + offset + " ," +
* 将SQL语句变成一条语句,并且每个单词的间隔都是1个空格
* @param sql
* @return 如果sql是NULL返回空,否则返回转化后的SQL
private static String getLineSql(String sql) {
return sql.replaceAll("[\r\n]", " ").replaceAll("\\s{2,}", " ");
* 得到SQL第一个正确的FROM的的插入点
private static int getAfterFormInsertPoint(String querySelect) {
String regex = "\\s+FROM\\s+";
Pattern pattern = pile(regex, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(querySelect);
while (matcher.find()) {
int fromStartIndex = matcher.start(0);
String text = querySelect.substring(0, fromStartIndex);
if (isBracketCanPartnership(text)) {
return fromStartI
* 判断括号"()"是否匹配,并不会判断排列顺序是否正确
* @param text
要判断的文本
* @return 如果匹配返回TRUE,否则返回FALSE
private static boolean isBracketCanPartnership(String text) {
if (text == null || (getIndexOfCount(text, '(') != getIndexOfCount(text, ')'))) {
* 得到一个字符在另一个字符串中出现的次数
* @param text
* @param ch
private static int getIndexOfCount(String text, char ch) {
int count = 0;
for (int i = 0; i & text.length(); i++) {
count = (text.charAt(i) == ch) ? count + 1 :
OracleDialect.java
package com.iflytek.mybatis.page.
* @author xdwang
下午7:54:56
* @description Oracle数据库实现
public class OracleDialect implements Dialect {
public String getPageSql(String sql, int offset, int limit) {
sql = sql.trim();
boolean isForUpdate =
if (sql.toLowerCase().endsWith(" for update")) {
sql = sql.substring(0, sql.length() - 11);
isForUpdate =
StringBuffer pageSql = new StringBuffer(sql.length() + 100);
pageSql.append("select * from ( select row_.*, rownum rownum_ from ( ");
pageSql.append(sql);
pageSql.append(" ) row_ ) where rownum_ & " + offset + " and rownum_ &= " + (offset + limit));
if (isForUpdate) {
pageSql.append(" for update");
return pageSql.toString();
SQLServerDialect.java
package com.iflytek.mybatis.page.
* @author xdwang
下午7:53:14
* @description SQLServer数据库实现
public class SQLServerDialect implements Dialect {
public String getPageSql(String sql, int offset, int limit) {
sql = sql.trim();
StringBuffer pageSql = new StringBuffer(sql.length() + 100);
// 其实这里还是有一点问题的,就是排序问题,指定死了,有解决的提供一下,等复习到Hibernate看看Hibernat内部是如何实现的。
pageSql.append("select * from(select a.*,row_number() over (order by id desc) rownum from( ");
pageSql.append(sql);
pageSql.append(") a )b where rownum& " + offset + " and rownum &= " + (offset + limit));
return pageSql.toString();
然后我们定义拦截器
PaginationInterceptor.java
package com.iflytek.mybatis.page.
import java.sql.C
import java.util.P
import mons.logging.L
import mons.logging.LogF
import org.apache.ibatis.executor.statement.StatementH
import org.apache.ibatis.mapping.BoundS
import org.apache.ibatis.plugin.I
import org.apache.ibatis.plugin.I
import org.apache.ibatis.plugin.I
import org.apache.ibatis.plugin.P
import org.apache.ibatis.plugin.S
import org.apache.ibatis.reflection.MetaO
import org.apache.ibatis.session.C
import org.apache.ibatis.session.RowB
import com.iflytek.mybatis.page.dialect.D
import com.iflytek.mybatis.page.dialect.MySql5D
import com.iflytek.mybatis.page.dialect.OracleD
* @author xdwang
下午8:01:31
* @description 然后就是实现mybatis提供的拦截器接口,编写我们自己的分页实现,原理就是拦截底层JDBC操作相关的Statement对象,
把前端的分页参数如当前记录索引和每页大小通过拦截器注入到sql语句中
,即在sql执行之前通过分页参数重新生成分页sql,而具体的分页sql实现是分离到Dialect接口中去。
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class PaginationInterceptor implements Interceptor {
private final static Log log = LogFactory.getLog(PaginationInterceptor.class);
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 =
databaseType = Dialect.Type.valueOf(configuration.getVariables().getProperty("dialect").toUpperCase());
} catch (Exception e) {
if (databaseType == null) {
throw new RuntimeException("the value of the dialect property in configuration.xml is not defined : " + configuration.getVariables().getProperty("dialect"));
Dialect dialect =
switch (databaseType) {
case MYSQL:
dialect = new MySql5Dialect();
case MSSQL:
dialect = new MySql5Dialect();
case ORACLE:
dialect = new OracleDialect();
dialect = new MySql5Dialect();
String originalSql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
metaStatementHandler.setValue("delegate.boundSql.sql", dialect.getPageSql(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();
public Object plugin(Object target) {
return Plugin.wrap(target, this);
public void setProperties(Properties properties) {
Ok,搞定了,下面看看如何使用,其实和直接调用MyBatis原生的假分页方式一样。只需要在mybatis-config.xml添加一个标识和一个插件
&properties&
&property name="dialect" value="mysql" /&
&/properties&
&plugin interceptor="com.iflytek.mybatis.page.interceptor.PaginationInterceptor"&
&/plugins&
然后和MyBatis默认提供分页的方式一样,直接调用
public List&Student& getStudentsByPage(){
List&Student& students = new ArrayList&Student&();
SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory().openSession();
//从第一条开始,取4条记录
RowBounds rowBounds = new RowBounds(1,4);
Student student=new Student();
student.setName("xdwang");
students = sqlSession.selectList("com.iflytek.dao.mapper.StudentMapper.selectByPageList", student, rowBounds);
} finally {
sqlSession.close();
,搞定,当然,上面我们也可以将需要拦截添加的
mapper.xml
中,然后再需要分页的查询语句中引用,只是需要在每个模块下分页的地方都引用,相对来说比较麻烦点(其实也还好)。
浏览 10981
xdwangiflytek
浏览: 345747 次
来自: 安徽
太复杂了而且不通用,利用ThreadLocal可完美解决这一问 ...
multipartRequest.getFiles(&quot ...
可惜没讲你mysql数据库的表的设计
效果还是挺不错的,谢谢了。mybatis实现物理分页 -
- ITeye技术网站
博客分类:
这两两天由于工作需要,对mybatis分页进行研究,现进行记录,主要从一下两个方面阐述。博主用的数据库是oracle,所以以下分页都是针对oracle的,mybatis版本是3.3.0
一.mybatis自带分页功能的效率与物理分页的效率对比
1.mybatis自带分页功能
通过查找资料,mybatis分页是逻辑分页,如果数据量大会造成内存溢出.mybatis自带分页实现如下
public List&WorkflowRecord& queryByMybatis(int startRow, int endRow) {
RowBounds rowBounds = new RowBounds(startRow,endRow);
System.out.println("rowBounds Offset:"+rowBounds.getOffset());
System.out.println("rowBounds Limit:"+rowBounds.getLimit());
WorkflowRecord workflowRecord=new WorkflowRecord();
return sqlSessionTemplate.selectList(QUERY_BY_MYBYTAIS,workflowRecord,rowBounds);
对应的sql为
&select id="queryByMybatis" resultMap="proRecResult"&
select * from T_PROREC
其中RowBounds的源码为
红线部分为默认值 下文还会提到,RowBounds的两个属性offset,
offset:从第n条开始取;limit:取多少条
2.物理分页
public List&WorkflowRecord& queryBySql(int startRow, int endRow) {
Map&String, Object& params =new HashMap&String, Object&();
params.put("startRow", new Integer(startRow));
params.put("endRow", new Integer(endRow));
return sqlSessionTemplate.selectList(QUERY_BY_SQL, params);
&select id="queryBySql" resultMap="proRecResult"&
from (SELECT ROWNUM r, t1.*
select * from T_PROREC ) t1
where rownum &= #{endRow})
where r &= #{startRow}
以上两种方式对比,本来以为mybatis效率会差,因为是查出所有数据,再取出相应的条数,但实际运行的结果是两种方式的差不多,没有太大的差异,不知道是不是因为T_PROREC表中数据太少的原因,表中大约有13000条数据。
二. mybatis 实现物理分页
博主采用了两种方式,一种是通过mybatis的include标签的应用,一种是通过mybatis的拦截器实现的
1.mybatis include标签
public List&WorkflowRecord& queryByMybatis(WorkflowRecord workflowRecord) {
return sqlSessionTemplate.selectList(QUERY_INCLUDE, workflowRecord);
传入对象包括startRow,endRow两个属性
&select id="queryInclude" resultMap="proRecResult"&
&include refid="com.hdnav.projectlibrary.entity.sqlMappers.pagination.paginationCommon1"/&
select * from T_PROREC
&include refid="com.hdnav.projectlibrary.entity.sqlMappers.pagination.paginationCommon2"/&
博主是将分页共同的代码单独的写在一另个sqlMapper里,方便所有的sqlMapper文件引用,代码如下
&?xml version="1.0" encoding="utf-8" ?&
&!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"&
&mapper namespace="com.hdnav.projectlibrary.entity.sqlMappers.pagination"&
&sql id="paginationCommon1" &
&![CDATA[ select *
from (SELECT ROWNUM r, t1.* from ( ]]&
&sql id="paginationCommon2" &
&![CDATA[ ) t1
where rownum &= #{endRow})
where r &= #{startRow}
2.mybatis拦截器实现
博主是参照这两篇文章实现的
/jcli/archive//2132222.html
http://chenjc-/blog/1402965
感谢以上两位博主
下面这篇文章是讲原理的,我只是实现了功能但没有仔细研究原理,大家可以看下
/blog/1851081
下面贴我实现的代码
public abstract class Dialect {
public static enum Type{
public abstract String getLimitString(String sql, int skipResults, int maxResults);
类OracleDialect
public class OracleDialect extends Dialect{
public String getLimitString(String sql, int offset, int limit) {
sql = sql.trim();
StringBuffer pagingSelect = new StringBuffer(sql.length() + 100);
pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
pagingSelect.append(sql);
pagingSelect.append(" ) row_ ) where rownum_ & ").append(offset).append(" and rownum_ &= ").append(offset + limit);
return pagingSelect.toString();
类PaginationInterceptor(mybatis监听器类)
import java.sql.C
import java.util.P
import org.apache.ibatis.executor.statement.StatementH
import org.apache.ibatis.mapping.BoundS
import org.apache.ibatis.plugin.I
import org.apache.ibatis.plugin.I
import org.apache.ibatis.plugin.I
import org.apache.ibatis.plugin.P
import org.apache.ibatis.plugin.S
import org.apache.ibatis.reflection.DefaultReflectorF
import org.apache.ibatis.reflection.MetaO
import org.apache.ibatis.reflection.ReflectorF
import org.apache.ibatis.reflection.factory.DefaultObjectF
import org.apache.ibatis.reflection.factory.ObjectF
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperF
import org.apache.ibatis.reflection.wrapper.ObjectWrapperF
import org.apache.ibatis.session.C
import org.apache.ibatis.session.RowB
import org.apache.logging.log4j.L
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
public class PaginationInterceptor implements Interceptor{
private static Logger logger = KJPTUtils.getLogger();
private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY= new DefaultReflectorFactory();
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
MetaObject metaStatementHandler = MetaObject.forObject(statementHandler,
DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY,DEFAULT_REFLECTOR_FACTORY);
RowBounds rowBounds = (RowBounds)metaStatementHandler.getValue("delegate.rowBounds");
if(rowBounds == null || rowBounds == RowBounds.DEFAULT){
return invocation.proceed();
String originalSql = (String)metaStatementHandler.getValue("delegate.boundSql.sql");
Configuration configuration = (Configuration)metaStatementHandler.getValue("delegate.configuration");
Dialect.Type databaseType
databaseType = Dialect.Type.valueOf(configuration.getVariables().getProperty("dialect").toUpperCase());
} catch (Exception e) {
if(databaseType == null){
throw new RuntimeException("the value of the dialect property in configuration.xml is not defined : "+ configuration.getVariables().getProperty("dialect"));
Dialect dialect =
switch(databaseType){
case ORACLE:
dialect = new OracleDialect();
case MYSQL://需要实现MySQL的分页逻辑
//改别要执行的sql语句,现在新设置的sql语句是物理分页的,所以现在不再需要mybatis进行额外的操作了,所以把rowBounds的偏移量恢复为初始值(offet:0,limit:Integer.max)
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 );
BoundSql boundSql = statementHandler.getBoundSql();
("生成分页SQL : " + boundSql.getSql());
return invocation.proceed();
public Object plugin(Object target) {
return Plugin.wrap(target, this);
public void setProperties(Properties properties) {
恢复到上文提到的RowBounds的默认值
mybatis.xml代码,配置mybatis的监听器
&properties&
&property name="dialect" value="oracle"/&
&/properties&
&plugin interceptor="mon.PaginationInterceptor"&&/plugin&
&/plugins&
配置完再调用下面dao层,拦截器会判断RowBounds是否为空,如果不为空,则重新构造新的sql,完成物理分页功能
public List&WorkflowRecord& queryByMybatis(int startRow, int endRow) {
RowBounds rowBounds = new RowBounds(startRow,endRow);
System.out.println("rowBounds Offset:"+rowBounds.getOffset());
System.out.println("rowBounds Limit:"+rowBounds.getLimit());
WorkflowRecord workflowRecord=new WorkflowRecord();
return sqlSessionTemplate.selectList(QUERY_BY_MYBYTAIS,workflowRecord,rowBounds);
mzl1989325
浏览: 956 次
恩,我这里只用了32毫秒
String s = &a ...深入浅出Mybatis-分页 - 博客频道 - CSDN.NET
追求极致的人
分享知识,共同进步!
分类:Mybatis
(121636)
Mybatis的分页功能很弱,它是基于内存的分页(查出所有记录再按偏移量和limit取结果),在大数据量的情况下这样的分页基本上是没有用的。本文基于插件,通过拦截StatementHandler重写sql语句,实现数据库的物理分页。本文适配的mybatis版本是3.2.2。
为什么在StatementHandler拦截
在章节介绍了一次sqlsession的完整执行过程,从中可以知道sql的解析是在StatementHandler里完成的,所以为了重写sql需要拦截StatementHandler。
MetaObject简介
在我的实现里大量使用了MetaObject这个对象,因此有必要先介绍下它。MetaObject是Mybatis提供的一个的工具类,通过它包装一个对象后可以获取或设置该对象的原本不可访问的属性(比如那些私有属性)。它有个三个重要方法经常用到:
1)&&&&&&&MetaObject forObject(Object object,ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory)
2)&&&&&&&Object getValue(String name)
3)&&&&&&&void setValue(String name, Object value)
方法1)用于包装对象;方法2)用于获取属性的值(支持OGNL的方法);方法3)用于设置属性的值(支持OGNL的方法);
插件的原理
有了上面这些基础知识的准备后,就可以我们的主题了。
拦截器签名
@Intercepts({@Signature(type =StatementHandler.class, method = &prepare&, args ={Connection.class})})
publicclass PageInterceptor implementsInterceptor {
从签名里可以看出,要拦截的目标类型是StatementHandler(注意:type只能配置成接口类型),拦截的方法是名称为prepare参数为Connection类型的方法。
intercept的实现
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaStatementHandler = MetaObject.forObject(statementHandler,
DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
// 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次循环
// 可以分离出最原始的的目标类)
while (metaStatementHandler.hasGetter(&h&)) {
Object object = metaStatementHandler.getValue(&h&);
metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY,
DEFAULT_OBJECT_WRAPPER_FACTORY);
// 分离最后一个代理对象的目标类
while (metaStatementHandler.hasGetter(&target&)) {
Object object = metaStatementHandler.getValue(&target&);
metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY,
DEFAULT_OBJECT_WRAPPER_FACTORY);
Configuration configuration = (Configuration) metaStatementHandler.
getValue(&delegate.configuration&);
dialect = configuration.getVariables().getProperty(&dialect&);
if (null == dialect || &&.equals(dialect)) {
logger.warn(&Property dialect is not setted,use default 'mysql' &);
dialect = defaultD
pageSqlId = configuration.getVariables().getProperty(&pageSqlId&);
if (null == pageSqlId || &&.equals(pageSqlId)) {
logger.warn(&Property pageSqlId is not setted,use default '.*Page$' &);
pageSqlId = defaultPageSqlId;
MappedStatement mappedStatement = (MappedStatement)
metaStatementHandler.getValue(&delegate.mappedStatement&);
// 只重写需要分页的sql语句。通过MappedStatement的ID匹配,默认重写以Page结尾的
MappedStatement的sql
if (mappedStatement.getId().matches(pageSqlId)) {
BoundSql boundSql = (BoundSql) metaStatementHandler.getValue(&delegate.boundSql&);
Object parameterObject = boundSql.getParameterObject();
if (parameterObject == null) {
throw new NullPointerException(&parameterObject is null!&);
// 分页参数作为参数对象parameterObject的一个属性
PageParameter page = (PageParameter) metaStatementHandler
.getValue(&delegate.boundSql.parameterObject.page&);
String sql = boundSql.getSql();
// 重写sql
String pageSql = buildPageSql(sql, page);
metaStatementHandler.setValue(&delegate.boundSql.sql&, pageSql);
// 采用物理分页后,就不需要mybatis的内存分页了,所以重置下面的两个参数
metaStatementHandler.setValue(&delegate.rowBounds.offset&,
RowBounds.NO_ROW_OFFSET);
metaStatementHandler.setValue(&delegate.rowBounds.limit&, RowBounds.NO_ROW_LIMIT);
Connection connection = (Connection) invocation.getArgs()[0];
// 重设分页参数里的总页数等
setPageParameter(sql, connection, mappedStatement, boundSql, page);
// 将执行权交给下一个拦截器
return invocation.proceed();
StatementHandler的默认实现类是RoutingStatementHandler,因此拦截的实际对象是它。RoutingStatementHandler的主要功能是分发,它根据配置Statement类型创建真正执行数据库操作的StatementHandler,并将其保存到delegate属性里。由于delegate是一个私有属性并且没有提供访问它的方法,因此需要借助MetaObject的帮忙。通过MetaObject的封装后我们可以轻易的获得想要的属性。
在上面的方法里有个两个循环,通过他们可以分离出原始的RoutingStatementHandler(而不是代理对象)。
前面提到,签名里配置的要拦截的目标类型是StatementHandler拦截的方法是名称为prepare参数为Connection类型的方法,而这个方法是每次数据库访问都要执行的。因为我是通过重写sql的方式实现分页,为了不影响其他sql(update或不需要分页的query),我采用了通过ID匹配的方式过滤。默认的过滤方式只对id以Page结尾的进行拦截(注意区分大小写),如下:
&select id=&queryUserByPage& parameterType=&UserDto& resultType=&UserDto&&
select * from t_user t where t.username = #{username}
当然,也可以自定义拦截模式,在mybatis的配置文件里加入以下配置项:
&properties&
&property name=&dialect& value=&mysql& /&
&property name=&pageSqlId& value=&.*Page$& /&
&/properties&
其中,属性dialect指示数据库类型,目前只支持mysql和oracle两种数据库。其中,属性pageSqlId指示拦截的规则,以正则方式匹配。
sql重写其实在原始的sql语句上加入分页的参数,目前支持mysql和oracle两种数据库的分页。
private String buildPageSql(String sql, PageParameter page) {
if (page != null) {
StringBuilder pageSql = new StringBuilder();
if (&mysql&.equals(dialect)) {
pageSql = buildPageSqlForMysql(sql, page);
} else if (&oracle&.equals(dialect)) {
pageSql = buildPageSqlForOracle(sql, page);
return pageSql.toString();
&mysql的分页实现:
public StringBuilder buildPageSqlForMysql(String sql, PageParameter page) {
StringBuilder pageSql = new StringBuilder(100);
String beginrow = String.valueOf((page.getCurrentPage() - 1) * page.getPageSize());
pageSql.append(sql);
pageSql.append(& limit & + beginrow + &,& + page.getPageSize());
return pageS
oracle的分页实现:
public StringBuilder buildPageSqlForOracle(String sql, PageParameter page) {
StringBuilder pageSql = new StringBuilder(100);
String beginrow = String.valueOf((page.getCurrentPage() - 1) * page.getPageSize());
String endrow = String.valueOf(page.getCurrentPage() * page.getPageSize());
pageSql.append(&select * from ( select temp.*, rownum row_id from ( &);
pageSql.append(sql);
pageSql.append(& ) temp where rownum &= &).append(endrow);
pageSql.append(&) where row_id & &).append(beginrow);
return pageS
分页参数重写
有时候会有这种需求,就是不但要查出指定页的结果,还需要知道总的记录数和页数。我通过重写分页参数的方式提供了一种解决方案:
* 从数据库里查询总的记录数并计算总页数,回写进分页参数&code&PageParameter&/code&,这样调用
* 者就可用通过 分页参数&code&PageParameter&/code&获得相关信息。
* @param sql
* @param connection
* @param mappedStatement
* @param boundSql
* @param page
private void setPageParameter(String sql, Connection connection, MappedStatement mappedStatement,
BoundSql boundSql, PageParameter page) {
// 记录总记录数
String countSql = &select count(0) from (& + sql + &) as total&;
PreparedStatement countStmt =
ResultSet rs =
countStmt = connection.prepareStatement(countSql);
BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,
boundSql.getParameterMappings(), boundSql.getParameterObject());
setParameters(countStmt, mappedStatement, countBS, boundSql.getParameterObject());
rs = countStmt.executeQuery();
int totalCount = 0;
if (rs.next()) {
totalCount = rs.getInt(1);
page.setTotalCount(totalCount);
int totalPage = totalCount / page.getPageSize() + ((totalCount % page.getPageSize() == 0) ? 0 : 1);
page.setTotalPage(totalPage);
} catch (SQLException e) {
logger.error(&Ignore this exception&, e);
} finally {
rs.close();
} catch (SQLException e) {
logger.error(&Ignore this exception&, e);
countStmt.close();
} catch (SQLException e) {
logger.error(&Ignore this exception&, e);
* 对SQL参数(?)设值
* @param ps
* @param mappedStatement
* @param boundSql
* @param parameterObject
* @throws SQLException
private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,
Object parameterObject) throws SQLException {
ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler.setParameters(ps);
plugin的实现
public Object plugin(Object target) {
// 当目标类是StatementHandler类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
下载地址:
排名:第14617名
(1)(0)(0)(0)(0)(1)(0)(1)(9)
(121629)
(109133)

我要回帖

更多关于 虎门北栅怎么读 的文章

 

随机推荐