mybatis分页pageHelper的源码查看

9 篇文章 0 订阅

mybatis在springboot的集成

集成springboot很简单,直接用 boot的starter即可,

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>

配置加上:

# Mysql 注意替换相应配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://xx.xx.xx.xx:3306/aaaa?useUnicode=true&characterEncoding=utf8
spring.datasource.username=aaaa
spring.datasource.password=aaaa

如果不是用springboot,则自己网上找下教程吧.

mybatis的自动生成xml

mybatis的sql都要自己写,比较麻烦,我们使用自动生成的方式
官网:http://www.mybatis.org/generator/index.html

使用步骤:
1,在pom.xml中增加插件配置

<!-- mybatis generator 自动生成代码插件 -->
<plugin>
	<groupId>org.mybatis.generator</groupId>
	<artifactId>mybatis-generator-maven-plugin</artifactId>
	<version>1.3.2</version>
	<configuration>
		<configurationFile>src/main/resources/mapper/generatorConfig.xml</configurationFile>
		<overwrite>true</overwrite>
		<verbose>true</verbose>
	</configuration>
</plugin>

2,把要相应的配置文件generatorConfig.xml加上

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <!-- 数据库驱动:选择你的本地硬盘上面的数据库驱动包-->
    <classPathEntry  location="src/main/resources/mapper/mysql-connector-java-5.1.46.jar"/>
    <context id="DB2Tables"  targetRuntime="MyBatis3">
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!--数据库链接URL,用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://xxxx:3306/aaa" userId="aaa" password="aaa">
        </jdbcConnection>
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
        <!-- 生成模型的包名和位置-->
        <javaModelGenerator targetPackage="com.saaa.bsss.test.model.po.db" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!-- 生成映射文件的包名和位置-->
        <sqlMapGenerator targetPackage="generator" targetProject="src/main/resources/mapper">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>
        <!-- 生成DAO的包名和位置-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.saaa.bsss.test.dao.mapper.generator" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!-- 要生成的表 tableName是数据库中的表名或视图名 domainObjectName是实体类名-->

        <table tableName="t_aaa" domainObjectName="AaaPO"></table>
        <table tableName="t_bbb" domainObjectName="BaaPO"></table>

    </context>
</generatorConfiguration>

注意要把 mysql-connector-java-5.1.46.jar加到工程中.同时上面所涉及到文件夹要存在。

3,执行mvn命令,生成相应的代码:

mybatis-generator:generate -e

MyBatis Generator是一个maven插件,其运行时并不依赖 spring或 mybatis,只是一个反向生成代码的工具,根据 db中的表进行映射和生成.

生成的代码运行例子

使用时,直接用生成的代码来处理,如

    @Autowired
    private UserPOExtMapper userDTOMapper;

     public   int insert(UserPO userDTO) {
        userDTO.setStatus(ProjectConstant.UserConstant.USER_STATUS_OK);
        userDTO.setCreateTime(new Date());
        return  userDTOMapper.insert(userDTO);
    }


    public UserPO findByUserid(Integer userid) {
        UserPOExample example = new UserPOExample();
        example.or().andUseridEqualTo(userid).andStatusEqualTo(ProjectConstant.UserConstant.USER_STATUS_OK);
        List<UserPO> userDTOS=  userDTOMapper.selectByExample(example);
        if (userDTOS == null || userDTOS.size() < 1) {
            return  null;
        }
        return userDTOS.get(0);
    }

mybatis的分页

分页,我们采用com.github.pagehelper

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

在使用时,

    public Page<AaaPO> findByUser(int userid,int pageNum, int pageSize) {
        Page<AaaPO> cpage = PageHelper.startPage(pageNum, pageSize);
        AaaPOExample example = new AaaPOExample();
        example.or();
        List<String> tostrList = new ArrayList<>();
        tostrList.add("all");
        tostrList.add(userid+"");
        example.or().andMToIn(tostrList);
        example.setOrderByClause("update_time desc");
        AaaPOMapper.selectByExample(example);
        return  cpage;
    }

说明, 如果我们看mapper.xml,会发现,里面是没有 数据库相应的 分页逻辑, 如mysql的limit, 但我们采用 com.github.pagehelper 却可以分页,这个很诡异的事情,同时com.github.pagehelper是用静态方法来做的,高并发时不会出错吗?后续这方面的相应源码.

com.github.pagehelper是一个开源的库,是一个mybatis的插件,主要作用就是在mybatis查询的时候,针对各数据库加上分页逻辑,同时返回。

mybatis 中pageHelper的源码

我们在
com.github.pagehelper.Page中的setTotal(long total)中打个断点,

stack如下:

setTotal:189, Page (com.github.pagehelper)
afterCount:89, AbstractHelperDialect (com.github.pagehelper.dialect)
afterCount:82, PageHelper (com.github.pagehelper)
intercept:117, PageInterceptor (com.github.pagehelper)
invoke:61, Plugin (org.apache.ibatis.plugin)
query:-1, $Proxy170 (com.sun.proxy)
selectList:148, DefaultSqlSession (org.apache.ibatis.session.defaults)
selectList:141, DefaultSqlSession (org.apache.ibatis.session.defaults)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:433, SqlSessionTemplate$SqlSessionInterceptor (org.mybatis.spring)
selectList:-1, $Proxy99 (com.sun.proxy)
selectList:230, SqlSessionTemplate (org.mybatis.spring)
executeForMany:139, MapperMethod (org.apache.ibatis.binding)
execute:76, MapperMethod (org.apache.ibatis.binding)
invoke:59, MapperProxy (org.apache.ibatis.binding)
selectByExample:-1, $Proxy135 (com.sun.proxy)
findByUseridAndAll:39, MyUserDAO (com.saaa.bsss.test.dao.db)
esTest:31, MyUserDAOTest (com.saaa.bsss.test.dao.db)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
runReflectiveCall:50, FrameworkMethod$1 (org.junit.runners.model)
run:12, ReflectiveCallable (org.junit.internal.runners.model)
invokeExplosively:47, FrameworkMethod (org.junit.runners.model)
evaluate:17, InvokeMethod (org.junit.internal.runners.statements)
evaluate:73, RunBeforeTestExecutionCallbacks (org.springframework.test.context.junit4.statements)
evaluate:83, RunAfterTestExecutionCallbacks (org.springframework.test.context.junit4.statements)
evaluate:75, RunBeforeTestMethodCallbacks (org.springframework.test.context.junit4.statements)
evaluate:86, RunAfterTestMethodCallbacks (org.springframework.test.context.junit4.statements)
evaluate:84, SpringRepeat (org.springframework.test.context.junit4.statements)
runLeaf:325, ParentRunner (org.junit.runners)
runChild:251, SpringJUnit4ClassRunner (org.springframework.test.context.junit4)
runChild:97, SpringJUnit4ClassRunner (org.springframework.test.context.junit4)
run:290, ParentRunner$3 (org.junit.runners)
schedule:71, ParentRunner$1 (org.junit.runners)
runChildren:288, ParentRunner (org.junit.runners)
access$000:58, ParentRunner (org.junit.runners)
evaluate:268, ParentRunner$2 (org.junit.runners)
evaluate:61, RunBeforeTestClassCallbacks (org.springframework.test.context.junit4.statements)
evaluate:70, RunAfterTestClassCallbacks (org.springframework.test.context.junit4.statements)
run:363, ParentRunner (org.junit.runners)
run:190, SpringJUnit4ClassRunner (org.springframework.test.context.junit4)
run:137, JUnitCore (org.junit.runner)
startRunnerWithArgs:68, JUnit4IdeaTestRunner (com.intellij.junit4)
startRunnerWithArgs:47, IdeaTestRunner$Repeater (com.intellij.rt.execution.junit)
prepareStreamsAndStart:242, JUnitStarter (com.intellij.rt.execution.junit)
main:70, JUnitStarter (com.intellij.rt.execution.junit)
会被调用的机制是什么

说明pageHelper是使用 mybatis的拦截器进行操作

上面的 invoke:61, Plugin (org.apache.ibatis.plugin)

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      if (methods != null && methods.contains(method)) {
        return interceptor.intercept(new Invocation(target, method, args));
      }
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }

其中上面的 interceptor 对象,就是page的拦截器!!

那拦截器是哪里被配置到mybatis的呢?

我们把pageHelper加到服务上,并没有增加显式的配置,说明有可能是用spring-boot的机制 来进行配置,
我们找到
pagehelper-spring-boot-autoconfigure-1.2.5.jar
中的 com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration 类
其中

    @PostConstruct
    public void addPageInterceptor() {
        PageInterceptor interceptor = new PageInterceptor();
        Properties properties = new Properties();
        properties.putAll(this.pageHelperProperties());
        properties.putAll(this.properties.getProperties());
        interceptor.setProperties(properties);
        Iterator var3 = this.sqlSessionFactoryList.iterator();

        while(var3.hasNext()) {
            SqlSessionFactory sqlSessionFactory = (SqlSessionFactory)var3.next();
            sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
        }

    }

上面的代码的 SqlSessionFactory 是org.apache.ibatis.session.SqlSessionFactory ,这个方法把interceptor初始化后扔到sqlSessionFactory中

@PostConstruct,这个annotation可以理解为,在这个方法在此类调用了construct后执行.

拦截器里面的逻辑?为什么用静态可以?

按图索骥,我们找到相应的拦截器
com.github.pagehelper.PageInterceptor

在Page cpage = PageHelper.startPage(pageNum, pageSize);时
往里走

protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();
...
LOCAL_PAGE.set(page);

这里说明这个page对象 是和线程绑定的,在代码运行的过程中,相应数据找到这个静态,通过线程找到对象 ,进行回填。

数据是怎么扔到page的?

逻辑上都是在 PageInterceptor中处理的,
回填是调用AbstractHelperDialect类的下面两个方法:

@Override
    public boolean afterCount(long count, Object parameterObject, RowBounds rowBounds) {
        Page page = getLocalPage();
        page.setTotal(count);
        if (rowBounds instanceof PageRowBounds) {
            ((PageRowBounds) rowBounds).setTotal(count);
        }
        //pageSize < 0 的时候,不执行分页查询
        //pageSize = 0 的时候,还需要执行后续查询,但是不会分页
        if (page.getPageSize() < 0) {
            return false;
        }
        return count > 0;
    }
@Override
    public Object afterPage(List pageList, Object parameterObject, RowBounds rowBounds) {
        Page page = getLocalPage();
        if (page == null) {
            return pageList;
        }
        page.addAll(pageList);
        if (!page.isCount()) {
            page.setTotal(-1);
        } else if ((page.getPageSizeZero() != null && page.getPageSizeZero()) && page.getPageSize() == 0) {
            page.setTotal(pageList.size());
        } else if(page.isOrderByOnly()){
            page.setTotal(pageList.size());
        }
        return page;
    }
mybatis分页对页数超出范围的处理

分页时,如果总11条数据,5条一页,则pageNum最大是3,如果我们传5,理论上是返回空数组。

中有一个设定,“分页合理化,针对不合理的页码自动处理”,即,如果传pageNum为5,分页时其会按3来查.具体源码如下:

com.github.pagehelper.Page

public void setTotal(long total) {
        this.total = total;
        if (total == -1) {
            pages = 1;
            return;
        }
        if (pageSize > 0) {
            pages = (int) (total / pageSize + ((total % pageSize == 0) ? 0 : 1));
        } else {
            pages = 0;
        }
        //分页合理化,针对不合理的页码自动处理
        if ((reasonable != null && reasonable) && pageNum > pages) {
            pageNum = pages;
            calculateStartAndEndRow();
        }
    }

如果要修改,设置 page.setReasonable(false); 即可

参考:

mybatis的插件怎么做?
http://www.mybatis.org/mybatis-3/configuration.html#plugins
中文:
http://www.mybatis.org/mybatis-3/zh/configuration.html#plugins

pageHelper官网
https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/en/HowToUse.md

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值