小工具      在线工具  汉语词典  css  js  c++  java

Mybatis分页pageHelper源码查看

java,mysql,mybatis,source 额外说明

收录于:40天前

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的限制,但是我们可以使用com.github.pagehelper来分页。这是一件很奇怪的事情。同时, com.github.pagehelper 是使用静态方法完成的吗?高并发的时候不会出错吗?接下来会给出这方面相应的源码。

com.github.pagehelper是一个开源库,也是mybatis的插件。它的主要作用是给每个数据库添加分页逻辑,并在mybatis查询时同时返回。

mybatis 中pageHelper的源码

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

堆栈如下:

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的拦截器来操作的。

以上调用:61,插件(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);
    }
  }

上面的拦截器对象就是页面的拦截器! !

那拦截器是哪里被配置到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。该方法初始化拦截器并将其扔到sqlSessionFactory中。

@PostConstruct,这个注解可以理解为这个方法是在这个类调用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的?

逻辑上都是在 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

. . .

相关推荐

额外说明

Java集合个人笔记组织

总体概括 -Java容器分为Collection和Map两个大类 -Collection接口的子接口有:List、Set、Queue接口,Map不是Collection子接口 -Map/Collection/Queue接口是所有集合框架的父接口 -Lis

额外说明

利用MyEclipse从数据库反向生成实体类之Hibernate方式

与框架hibernate紧密相关的利用利用MyEclipse从数据库反向生成实体类之Hibernate方式 第一个大步骤我想再重复说下 window-->open Perspective-->MyEclipse Java Persistence  进行了

额外说明

leetcode679(24点游戏:回溯法)

你有 4 张写有 1 到 9 数字的牌。你需要判断是否能通过 *,/,+,-,( ) 的运算得到 24。 输入: [4, 1, 8, 7] 输出: True 解释: (8-4) * (7-1) = 24 题解:穷举法,无脑列出所有可能的情况,一个一个判断

额外说明

Linux 配置 VNC 远程桌面

目录 - 前言 ☀️ VNC 服务端配置 配置本地 yum 源 安装 vnc 服务端软件 ⭐️ VNC 客户端配置 下载 VNC 客户端软件 VNC 客户端连接 - 前言 Linux 主机如果不是虚拟机安装,那么很难直接访问图形化界面。 通常都是使用 S

额外说明

如何查看centos7系统的服务器ip地址

    输入ip查询命名 ip addr  也可以输入 ifconfig(centOs7没有ifconfig命令)查看ip,但此命令会出现3个条目,centos的ip地址是ens33条目中的inet值。   发现 ens33 没有 inet 这个属性,那

额外说明

【Rust 基础篇】Rust Deref Trait 的使用

导言 在 Rust 中,Deref trait 是一种特殊的 trait,用于重载解引用操作符 *。通过实现 Deref trait,我们可以定义类型的解引用行为,使其在使用 * 运算符时表现得像引用类型。 本篇博客将详细介绍 Rust 中如何实现和使用

额外说明

MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《9》

MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《1》:论文源地址,克隆MXNet版本的源码,安装环境与测试,以及对下载的源码的每个目录做什么用的,做个解释。 MXNet的Faster R-CNN(基于区域提议网络的实时目标检测)《

额外说明

静态方法,或工具类如何调用Spring管理的对象,调用Dao,Service接口方法时,普通调用方式报空指针异常:解决方案。

由于是static方法去调用的是Spring管理的对象,而且static方法不能调用非静态的方法,和属性。 因为static方法不依赖对象的创建,在类加载的时候就被加载进内存,因此static调用dao接口或service接口里的方法时,接口的实现类也许

额外说明

wordpress字体_如何在WordPress帖子编辑器中使用图标字体(无需HTML)

WordPress 字体 Icon fonts are vector icons used as fonts. They are popular among web designers because they look prettier than bi

额外说明

Curator与zookeeper版本问题,分布式锁

1、curator和zookeeper版本如下pom.xml <!-- zookeeper --> <dependency> <groupId>org.apache.zookeeper</group

ads via 小工具