6.Mybatis分页插件(PageHelper),解决PageHelper.startPage()不安全分页

这篇具有很好参考价值的文章主要介绍了6.Mybatis分页插件(PageHelper),解决PageHelper.startPage()不安全分页。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录


Mybatis专栏目录(点击进入…)


@TOC


Mybatis分页插件(PageHelper)

1.引入分页插件(依赖/Jar)

引入分页插件有下面2种方式,推荐使用Maven方式

(1)引入Jar包

可以从下面的地址中下载最新版本的jar包
https://oss.sonatype.org/content/repositories/releases/com/github/pagehelper/pagehelper/

http://repo1.maven.org/maven2/com/github/pagehelper/pagehelper/

由于使用了sql解析工具,还需要下载jsqlparser.jar:
http://repo1.maven.org/maven2/com/github/jsqlparser/jsqlparser/0.9.5/

(2)使用Maven在pom.xml中添加如下依赖。此版本依赖mybatis 3.4.6

<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper</artifactId>
	<version>5.1.8</version>
</dependency>
<!-- 编译依赖项 -->
<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>1.2</version>
</dependency>

最新版本号可以从首页查看

(3)Spring Boot Starter(启动器)

//推荐使用下面这种方式
<!--pagehelper-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.3</version>
</dependency>

2.配置拦截器插件

特别注意,新版拦截器是com.github.pagehelper.PageInterceptor
com.github.pagehelper.PageHelper现在是一个特殊的dialect实现类,是分页插件的默认实现类,提供了和以前相同的用法

(1)在MyBatis核心配置文件(Xml)中配置拦截器插件

<plugins>
	<!-- com.github.pagehelper为PageHelper类所在包名 -->
	<plugin interceptor="com.github.pagehelper.PageInterceptor">
		<property name="helperDialect" value="mysql" />
	</plugin>
</plugins>

plugins在配置文件中的位置必须符合要求(顺序),否则会报错
顺序如下:
①properties
②settings
③typeAliases
④typeHandlers
⑤objectFactory
⑥objectWrapperFactory
⑦plugins
⑧environments
⑨databaseIdProvider
⑩mapper


(2)在Spring配置文件中配置拦截器插件

使用Spring的属性配置方式,可以使用plugins属性像下面这样配置:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<!-- 注意其他配置 -->
	<property name="plugins">
		<array>
			<bean class="com.github.pagehelper.PageInterceptor">
				<property name="properties">
					<!--使用下面的方式配置参数,一行配置一个 -->
					<value>
						<!--使用的数据库类型 -->
						helperDialect=mysql
						reasonable=true
						supportMethodsArguments=true
						params=count=countSql
						autoRuntimeDialect=true
					</value>
				</property>
			</bean>
		</array>
	</property>
</bean>

(3)Spring Boot(application.properties)

#分页插件
pagehelper.helper-dialect=mysql
pagehelper.params=count=countSql
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true

分页插件参数介绍

分页插件提供了多个可选参数,这些参数使用时,按照上面两种配置方式中的示例配置即可

dialect:默认情况下会使用PageHelper方式进行分页,如果想要实现自己的分页逻辑,可以实现 Dialect(com.github.pagehelper.Dialect)接口,然后配置该属性为实现类的全限定名称

下面几个参数都是针对默认dialect情况下的参数。使用自定义dialect实现时,下面的参数没有任何作用

(1)helperDialect:数据库方言

分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。可以配置helperDialect属性来指定分页插件使用哪种方言

配置时,可以使用下面的缩写值:
oracle、mysql、mariadb、sqlite、hsqldb、postgresql、db2
sqlserver、informix、h2、sqlserver2012、derby

特别注意:使用Sql Server 2012数据库时,需要手动指定为Sql Server 2012,否则会使用Sql Server 2005的方式进行分页。也可以实现AbstractHelperDialect,然后配置该属性为实现类的全限定名称即可使用自定义的实现方法。

(2)offsetAsPageNum:RowBounds的offset作pageNum

该参数对使用RowBounds作为分页参数时有效。当该参数设置为true时,会将RowBounds中的offset参数当成pageNum使用,可以用页码和页面大小两个参数进行分页(默认值为false)

(3)rowBoundsWithCount:RowBounds进行count查询

该参数对使用RowBounds作为分页参数时有效。当该参数设置为true时,使用RowBounds分页会进行count查询(默认值为false)

(4)pageSizeZero:查询全部结果

当该参数设置为true时。如果pageSize = 0或者RowBounds.limit = 0就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是Page类型)(默认值为false)

(5)reasonable:查询第一页、最后一页、参数查询

分页合理化参数,默认值为false。当该参数设置为true时,pageNum<=0时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。为false时,直接根据参数进行查询

(6)params:参数映射

为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值,可以配置pageNum、pageSize、count、pageSizeZero、reasonable

不配置映射的用默认值,默认值为

pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
(7)supportMethodsArguments

支持通过Mapper接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面params配置的字段中取值,查找到合适的值时就会自动分页。使用方法可以参考测试代码中的com.github.pagehelper.test.basic包下的ArgumentsMapTest和ArgumentsObjTest

(8)autoRuntimeDialect

设置为true时,允许在运行时根据多数据源自动识别对应方言的分页(不支持自动选择sqlserver2012,只能使用sqlserver),用法和注意事项参考下面的场景五(默认值为 false)

(9)closeConn:是否关闭数据库连接

当使用运行时动态数据源或没有设置helperDialect属性自动获取数据库类型时,会自动获取一个数据库连接,通过该属性来设置是否关闭获取的这个连接,默认true关闭,设置为false后,不会关闭获取的连接,这个参数的设置要根据自己选择的数据源来决定(默认值为true)

重要提示:
当offsetAsPageNum=false的时候,由于PageNum问题,RowBounds查询的时候reasonable会强制为false。使用PageHelper.startPage()不受影响


如何选择配置这些参数
场景一

如果仍然在用类似Mybatis式的命名空间调用方式,也许会用到rowBoundsWithCount,分页插件对RowBounds支持和MyBatis默认的方式是一致,默认情况下不会进行count查询,如果想在分页查询时进行count查询,以及使用更强大的PageInfo类,需要设置该参数为true
注意:PageRowBounds想要查询总数也需要配置该属性为true

场景二

如果仍然在用类似Mybatis式的命名空间调用方式,RowBounds中的两个参数offset、limit不如 pageNum、pageSize容易理解,可以使用offsetAsPageNum参数,将该参数设置为true后,offset会当成pageNum使用,limit和pageSize含义相同

场景三

如果觉得某个地方使用分页后,仍然想通过控制参数查询全部的结果,可以配置pageSizeZero为 true,配置后,当pageSize=0或者RowBounds.limit = 0就会查询出全部的结果

场景四

如果分页插件使用于类似分页查看列表式的数据。如新闻列表、软件列表,希望用户输入的页数不在合法范围(第一页到最后一页之外)时能够正确的响应到正确的结果页面,那么可以配置reasonable为true,这时如果pageNum<=0会查询第一页,如果pageNum>总页数会查询最后一页

场景五

如果在Spring中配置了动态数据源。并且连接不同类型的数据库,这时可以配置 autoRuntimeDialect为true,这样在使用不同数据源时,会使用匹配的分页进行查询。这种情况下,还需要特别注意closeConn参数,由于获取数据源类型会获取一个数据库连接,所以需要通过这个参数来控制获取连接后,是否关闭该连接。 默认为true,有些数据库连接关闭后就没法进行后续的数据库操作。而有些数据库连接不关闭就会很快由于连接数用完而导致数据库无响应。所以在使用该功能时,特别需要注意你使用的数据源是否需要关闭数据库连接。

当不使用动态数据源而只是自动获取 helperDialect 时,数据库连接只会获取一次,所以不需要担心占用的这一个连接是否会导致数据库出错,但是最好也根据数据源的特性选择是否关闭连接


分页注意
(1)PageHelper.startPage()重要提示

只有紧跟在PageHelper.startPage方法后的第一个Mybatis的查询(Select)方法会被分页

(2)不要配置多个分页插件

请不要在系统中配置多个分页插件(使用Spring时。mybatis-config.xml和Spring配置方式,选择其中一种,不要同时配置多个分页插件)

(3)分页插件不支持带有for update语句的分页

对于带有for update的sql,会抛出运行时异常,对于这样的sql建议手动分页,毕竟这样的sql需要重视

(4)分页插件不支持嵌套结果映射

由于嵌套结果方式会导致结果集被折叠,因此分页查询的结果在折叠后总数会减少,所以无法保证分页结果数量正确


3.常用分页方式详细

(1)RowBounds方式的调用(mybatis)

Mybatis提供了一个简单的逻辑分页使用类RowBounds,在DefaultSqlSession提供的某些查询接口中我们可以看到RowBounds是作为参数用来进行分页,逻辑分页会将所有的结果都查询到,然后根据RowBounds中提供的offset和limit值来获取最后的结果。

RowBounds();
RowBounds(int offset, int limit);  

参数:
offset:偏移量
limit:每页展示多少数据

List<Student> list = studentMapper.find(new RowBounds(0, 10));
Page page = ((Page) list;

Page<Student>  page = studentMapper.find(new RowBounds(0, 10));

mappep.xml里面正常配置,不用对rowBounds任何操作。mybatis的拦截器自动操作rowBounds进行分页。使用RowBounds最大好处就是节省了在xml再拼装limit
总结:Mybatis的逻辑分页比较简单,简单来说就是取出所有满足条件的数据,然后舍弃掉前面offset条数据,然后再取剩下的数据的limit条

使用这种调用方式时,可以使用RowBounds参数进行分页,这种方式侵入性最小,可以看到,通过RowBounds方式调用只是使用了这个参数,并没有增加其他任何内容

分页插件检测到使用了RowBounds参数时,就会对该查询进行物理分页。关于这种方式的调用,有两个特殊的参数是针对RowBounds(场景一和场景二)

注意:不只有命名空间方式可以用RowBounds,使用接口的时候也可以增加RowBounds参数。

// 这种情况下也会进行物理分页查询
List<Country> selectAll(RowBounds rowBounds);  

注意:由于默认情况下的RowBounds无法获取查询总数,分页插件提供了一个继承自RowBounds的PageRowBounds,这个对象中增加了total属性,执行分页查询后,可以从该属性得到查询总数。


(2)PageHelper.offsetPage()静态方法

//start:开始处,rowNum:数目
PageHelper.offsetPage(int offset, int limit);
List<User> list = userMapper.selectAll();

跟startPage()使用一样,只是限制、参数不同。

(3)PageHelper.startPage静态方法调用(推荐)

除了PageHelper.startPage方法外,还提供了类似用法的PageHelper.offsetPage方法
在需要进行分页的MyBatis查询方法前调用PageHelper.startPage静态方法即可,紧跟在这个方法后的第一个MyBatis查询方法会被进行分页

//获取第1页,10条内容,默认查询总数count
PageHelper.startPage(1, 10);
//紧跟着的第一个select方法会被分页,后面的不会被分页,除非再次调用PageHelper.startPage()
List<User> list = userMapper.selectAll();

//分页时,实际返回的结果list类型是Page<E>,如果想取出分页信息,需要强制转换为Page<E>或者使用PageInfo类包装
// ①Page
Page page = (Page) list;
page.getTotal();

// ②PageInfo
PageInfo page = new PageInfo(list);
page.getTotal();

(4)使用参数方法方式

想要使用参数方式,需要配置supportMethodsArguments参数为 true,同时要配置params参数。

<plugins>
	<!-- com.github.pagehelper为PageHelper类所在包名 -->
	<plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- 配置supportMethodsArguments=true -->
		<property name="supportMethodsArguments" value="true" />
		<property name="params"
			value="pageNum=pageNumKey;pageSize=pageSizeKey;" />
	</plugin>
</plugins>

在MyBatis Mapper方法中

List<Country> selectByPageNumSize(
	@Param("user") User user,
	@Param("pageNumKey") int pageNum,
	@Param("pageSizeKey") int pageSize);

// 在代码中直接调用:
List<User> userList = userMapper.selectByPageNumSize(user, 1, 10);

当调用这个方法时,由于同时发现了pageNumKey和pageSizeKey参数,这个方法就会被分页。params 提供的几个参数都可以这样使用

(5)参数对象方式

除了上面这种方式外,如果 User 对象中包含这两个参数值,也可以有下面的方法:

// 如果pageNum和pageSize存在于User对象中,只要参数有值,也会被分页
public class User {
	// 其他fields
	// 下面两个参数名和params配置的名字一致
	private Integer pageNum;
	private Integer pageSize;
}

// 存在以下Mapper接口方法,不需要在xml处理后两个参数
public interface UserMapper {
	List<User> selectByPageNumSize(User user);
}

// 当user中的pageNum!=null&&pageSize!=null时,会自动分页
List<User> listUser = userMapper.selectByPageNumSize(user);

当从User中同时发现了pageNum Key和pageSize Key参数,这个方法就会被分页

注意:pageNum和pageSize两个属性同时存在才会触发分页操作,在这个前提下,其他的分页参数才会生效。

(6)ISelect接口方式

//第六种,ISelect 接口方式
//jdk6,7用法,创建接口
Page<Country> page = PageHelper.startPage(1, 10).doSelectPage(new ISelect() {
    @Override
    public void doSelect() {
        countryMapper.selectGroupBy();
    }
});

//jdk8 lambda用法
Page<Country> page = PageHelper.startPage(1, 10).doSelectPage(()-> countryMapper.selectGroupBy());

//也可以直接返回PageInfo,注意doSelectPageInfo方法和doSelectPage
pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(new ISelect() {
    @Override
    public void doSelect() {
        countryMapper.selectGroupBy();
    }
});

//对应的lambda用法
pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(() -> countryMapper.selectGroupBy());

//count查询,返回一个查询语句的count数
long total = PageHelper.count(new ISelect() {
    @Override
    public void doSelect() {
        countryMapper.selectLike(country);
    }
});

//lambda
total = PageHelper.count(()->countryMapper.selectLike(country));

PageHelper安全调用

1.使用RowBounds和PageRowBounds参数方式是极其安全的

2.使用参数方式是极其安全的

3.使用ISelect接口调用是极其安全的

ISelect接口方式除了可以保证安全外,还特别实现了将查询转换为单纯的count查询方式,这个方法可以将任意的查询方法,变成一个select count(*)的查询方法。


PageHelper.startPage()什么时候会导致不安全的分页?解决?

PageHelper.startPage()方法使用了静态的ThreadLocal参数,分页参数和线程是绑定的。只要可以保证在PageHelper方法调用后紧跟MyBatis查询方法,就是安全的。因为PageHelper在finally代码段中自动清除了ThreadLocal存储的对象

如果代码在进入Executor前发生异常,就会导致线程不可用,这属于人为的Bug(例如接口方法和XML中的不匹配,导致找不到MappedStatement 时),这种情况由于线程不可用,也不会导致ThreadLocal参数被错误的使用。

(1)不安全的用法

PageHelper.startPage(1, 10);
List<User> list;
if (param != null) {
	list = userMapper.selectAll(param);
} else {
	list = new ArrayList<User>();
}

这种情况下由于param存在null的情况,就会导致PageHelper生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。

上面这个代码,应该写成下面这个样子:

List<User> list;
if (param != null) {
	PageHelper.startPage(1, 10);
	list = userMapper.selectAll(param);
} else {
	list = new ArrayList<User>();
}

这种写法就能保证安全

如果对此不放心,可以手动清理ThreadLocal存储的分页参数

List<User> list;
if (param != null) {
	PageHelper.startPage(1, 10);
	try {
		list = userMapper.selectAll(param);
	} finally {
		PageHelper.clearPage();
	}
} else {
	list = new ArrayList<User>();
}

这么写很不好看,而且没有必要。文章来源地址https://www.toymoban.com/news/detail-775889.html

到了这里,关于6.Mybatis分页插件(PageHelper),解决PageHelper.startPage()不安全分页的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring集成【MyBatis】和【PageHelper分页插件】整合---详细介绍

        Spring 整合 MyBatis 是将 MyBatis 数据访问框架与 Spring 框架进行集成,以实现更便捷的开发和管理。在集成过程中,Spring 提供了许多特性和功能,如依赖注入、声明式事务管理、AOP 等 便捷的配置管理:     通过 Spring 的 XML 配置文件,我们可以集中管理 MyBatis 的配置信息、

    2024年02月11日
    浏览(56)
  • Spring与Mybatis整合&&aop整合pageHelper分页插件

    Spring与MyBatis整合 的意义在于提供了一种结合优势的方式,以便更好地 开发和管理持久层(数据库访问)代码 。 这里也是总结了几点主要意义 简化配置:Spring与MyBatis整合后,可以通过Spring的配置文件来管理和配置MyBatis的相关配置,例如数据源、事务管理等,而不需要额外

    2024年02月11日
    浏览(85)
  • MyBatis与Spring整合以及AOP和PageHelper分页插件整合

    目录 前言 一、MyBatis与Spring整合的好处以及两者之间的关系 1.好处 2.关系  二、MyBatis和Spring集成 1.导入pom.xml 2.编写配置文件  3.利用mybatis逆向工程生成模型层代码 三、常用注解  四、AOP整合pageHelper分页插件 创建一个切面 测试 MyBatis是一个开源的持久层框架,而Spring是一个

    2024年02月11日
    浏览(46)
  • MyBatis与Spring集成&常用注解以及AOP和PageHelper分页插件整合

    目录 前言 一、MyBatis与Spring整合的好处以及两者之间的关系 1.好处 2.关系  二、MyBatis和Spring集成 1.导入pom.xml 2.编写配置文件  3.利用mybatis逆向工程生成模型层代码 三、常用注解  四、AOP整合pageHelper分页插件 创建一个切面 测试 MyBatis是一个开源的持久层框架,而Spring是一个

    2024年02月07日
    浏览(60)
  • 7.3 SpringBoot整合MyBatis分页插件github.pageHelper:实现图书列表API

    在软件开发中,分页是一个非常常见的需求,无论是在Web应用程序还是在移动应用程序中,我们经常需要将大量的数据分成多个页面进行展示。 本文主要实现图书列表API,使用 SpringBoot集成MyBatis分页插件github.pageHelper ,首先会从「 自己实现分页原理 」说起,再到「 使用gi

    2024年02月11日
    浏览(61)
  • MyBatis之分页查询:MyBatis PageHelper

    MyBatis,作为目前流行的ORM框架,大大方便了日常开发。而对于分页查询,虽然可以通过SQL的limit语句实现,但是比较繁琐。而MyBatis PageHelper的出现,则解决了这一痛点。这里将介绍如何在Spring Boot、MyBatis的环境中通过MyBatis PageHelper高效方便的实现分页查询 添加Maven依赖 添加配

    2024年02月09日
    浏览(45)
  • SpringBoot分页插件(PageHelper)

    Mybatis给开发者提供了一个拦截器接口,只要实现了该接口,就可以在Mybatis执行SQL前,作一些自定义的操作。分页插件就是在此基础上开发出来的,对于一个需要分页的SQL,插件会拦截并生成两段SQL。举一个简单的例子 这样我们只需要根据业务逻辑开发原SQL,不需关心分页语

    2024年04月11日
    浏览(47)
  • 后端项目开发:分页功能的实现(Mybatis+pagehelper)

    分页查询是项目中的常用功能,此处我们基于Mybatis对分页查询进行处理。 引入分页依赖 在http目录下,新建PageResult类,我们用此类包装分页结果。

    2024年02月11日
    浏览(43)
  • SpringBoot第26讲:SpringBoot集成MySQL - MyBatis PageHelper分页

    前文中,我们展示了Spring Boot与MyBatis的集成,但是没有展示分页实现。本文是SpringBoot第26讲,专门介绍分页相关知识体系和基于MyBatis的 物理分页PageHelper

    2024年02月13日
    浏览(46)
  • 【SpringBoot】MyBatis与MyBatis-Plus分页查询 & github中的PageHelper

            笔者写这篇博客是因为近期遇到的关于两者之间的分页代码差距,其实之前也遇见过但是没有去整理这篇博客,但由于还是被困扰了小一会儿时间,所以还是需要 加深记忆 。其实会看前后端传参解决这个问题很快、不麻烦。关于这两个框架的分页代码问题主要就

    2024年02月03日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包