spring + mybatis + c3p0 整合(mybatis分页)

摘要

近期由于项目使用mybatis出现了数据源阻塞,导致应用程序假死,服务超时引发严重后果,故此下定决心重新梳理一下spring+mybatis+c3p0整合问题,主要分为:配置、源码(通过一次数据库操作分析)、myabatis缓存、问题总结
使用版本spring 4.1.7.RELEASE、mybatis 3.3.0、mybatis-spring 1.2.3

spring + mybatis + c3p0 整合(配置篇)
spring + mybatis + c3p0 整合(源码分析)
spring + mybatis + c3p0 整合(源码分析-mybati核心对象)

概述

分页查询就是将数据库查询的结果在有限的界面上分好多页显示;可以分为逻辑分页和物理分页

  • 逻辑分页:数据库返回的是全部数据,在通过代码实现分页
  • 物理分页:使用数据库自身所带的分页机制,返回分页数据;如Oracle使用的rownum,Mysql使用的limit等机制完成分页操作

建议日常工作中尽量使用物理分页;逻辑分页一般会消耗过多内存

RowBounds分页

mybatis提供可以进行逻辑分页的RowBounds类,任何select都可以使用它,但是该分页操作是对ResultSet结果进行分页,即逻辑分页,先看下RowBounds源码

public class RowBounds {

  public static final int NO_ROW_OFFSET = 0;
  public static final int NO_ROW_LIMIT = Integer.MAX_VALUE;
  public static final RowBounds DEFAULT = new RowBounds();

  private int offset;
  private int limit;
 
  public RowBounds() {
    this.offset = NO_ROW_OFFSET;
    this.limit = NO_ROW_LIMIT;
  }
  // 带参数构造函数,实现逻辑分页
  public RowBounds(int offset, int limit) {
    this.offset = offset;
    this.limit = limit;
  }

  public int getOffset() {
    return offset;
  }

  public int getLimit() {
    return limit;
  }

}

从源码可以看出定义了两个参数offset、limit;offset表示从第几行开始读取,limit表示限制返回条数;limit默认为最大整数

分页原理

从DefaultSqlSession源码可以看出,其提供的查询接口是以RowBounds作为参数用来进行分页的

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)

再看DefaultResultSetHandler结果集处理器,可以看出RowBounds分页是把offset之前数据跳过,超过limit之后数据不取出实现的;从这里也可以看出其实是先查询所有结果集,再从结果集中取出offset到limit之间数据实现分页

DefaultResultContext<Object> resultContext = new DefaultResultContext();
        // 跳到offset位置,准备读取数据
        this.skipRows(rsw.getResultSet(), rowBounds);
        Object rowValue = null;
		// while循环判断是否小于limit值,如果是读取limit条数据
        while(this.shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
            ResultMap discriminatedResultMap = this.resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, (String)null);
            // 省略代码
                }
            }
        }

private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
        if(rs.getType() != 1003) {
            if(rowBounds.getOffset() != 0) {
               // 直接定位
                rs.absolute(rowBounds.getOffset());
            }
        } else {
            // 通过循环跳到offset位置,进行读取
            for(int i = 0; i < rowBounds.getOffset(); ++i) {
                rs.next();
            }
        }

    }

分页插件PageHelper

spring配置方式

引入依赖

<dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.2</version>
</dependency>

xml配置

<!--sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
         <!-- pageHelper插件  -->
        <property name="plugins">
            <array>
                <bean class="com.github.pagehelper.PageInterceptor">
                    <property name="properties">
                        <!--使用下面的方式配置参数,一行配置一个 -->
                        <value>
                            helperDialect=mysql
                        </value>
                    </property>
                </bean>
            </array>
        </property>
    </bean>

SpringBoot

引入依赖

<dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper-spring-boot-starter</artifactId>
      <version>1.3.0</version>
  </dependency>

配置

pagehelper:
  helperDialect: mysql
  reasonable: false
  supportMethodsArguments: true
  params: count=countSql

使用

在查询方法前执行

PageHelper.startPage(dto.getPageNum(), dto.getPageSize());

该方法后紧跟需要分页查询的sql,否则会导致其他不需要分页分页

原理

PageHelper实现分页主要依赖ThreadLocal和Mybatis插件技术;从前文四大核心对象创建知道,mybatis创建对象返回的不是原始对象,是经过包装之后的代理对象

this.interceptorChain.pluginAll(executor);

interceptor.plugin(target)

该方法获取所有实现插件Interceptor接口的实现类,如PageInterceptor,调用其plugin方法返回包装后的代理对象

在执行查询sql时会执行PageInterceptor的intercept方法,判断是否需要生成分页sql是通过从ThreadLocal中是否存在Page对象,这里就涉及前面的startPage方法,其会创建Page对象set到ThreadLocal中;

对源码有兴趣可自行debug

intercept方法部分源码

public Object intercept(Invocation invocation) throws Throwable {
        try {
            ....
            // 判断是否需要分页
            if(!this.dialect.skip(ms, parameter, rowBounds)) {
                if(this.dialect.beforeCount(ms, parameter, rowBounds)) {
                    Long count = this.count(executor, ms, parameter, rowBounds, (ResultHandler)null, boundSql);
                    if(!this.dialect.afterCount(count.longValue(), parameter, rowBounds)) {
                        Object var12 = this.dialect.afterPage(new ArrayList(), parameter, rowBounds);
                        return var12;
                    }
                }
			   // 执行分页查询
                resultList = ExecutorUtil.pageQuery(this.dialect, executor, ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
            } else {
                resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            }

            Object var16 = this.dialect.afterPage(resultList, parameter, rowBounds);
            return var16;
        } finally {
            if(this.dialect != null) {
            	// 清除threadLocal
                this.dialect.afterAll();
            }

        }
    }

返回分页数据注意踩坑,可以创建工具类操作

public static CommonPageResponse bulidPageResponse(List list){
        CommonPageResponse response =  new CommonPageResponse();
        PageInfo<Object> pageInfo = new PageInfo(list);
        response.setPageNum(pageInfo.getPageNum());
        response.setPageSize(pageInfo.getPageSize());
        response.setTotalCount((int)pageInfo.getTotal());
        response.setTotalPages(pageInfo.getPages());
        response.setData(list);
        return response;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值