mybatis分页的方式有哪些


本篇内容主要讲解“mybatis分页的方式有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“mybatis分页的方式有哪些”吧! mybatis分页的方式:1、借助数组进行分页,首先查询出全部数据,然后再list中截取需要的部分。2、借助Sql语句进行分页,在sql语句后面添加limit分页语句即可。3、利用拦截器分页,通过拦截器给sql语句末尾加上limit语句来分页查询。4、利用RowBounds实现分页,需要一次获取所有符合条件的数据,然后在内存中对大数据进行操作即可实现分页效果。mybatis的分页方式可分为大的两类:1、逻辑分页2、物理分页借助数组进行分页(逻辑分页)借助Sql语句进行分页(物理分页)拦截器分页 (物理分页) 通过拦截器给sql语句末尾加上limit语句来查询,一劳永逸最优RowBounds实现分页 (逻辑分页)1、借助数组进行分页原理:进行数据库查询操作时,获取到数据库中所有满足条件的记录,保存在应用的临时数组中,再通过List的subList方法,获取到满足条件的所有记录。实现:首先在dao层,创建StudentMapper接口,用于对数据库的操作。在接口中定义通过数组分页的查询方法,如下所示:

ListqueryStudentsByArray();

方法很简单,就是获取所有的数据,通过list接收后进行分页操作。创建StudentMapper.xml文件,编写查询的sql语句:

可以看出再编写sql语句的时候,我们并没有作任何分页的相关操作。这里是查询到所有的学生信息。接下来在service层获取数据并且进行分页实现:定义IStuService接口,并且定义分页方法:

ListqueryStudentsByArray(intcurrPage,intpageSize);

通过接收currPage参数表示显示第几页的数据,pageSize表示每页显示的数据条数。创建IStuService接口实现类StuServiceIml对方法进行实现,对获取到的数组通过currPage和pageSize进行分页:

@Override
publicListqueryStudentsByArray(intcurrPage,intpageSize){
Liststudents=studentMapper.queryStudentsByArray();
//从第几条数据开始
intfirstIndex=(currPage-1)*pageSize;
//到第几条数据结束
intlastIndex=currPage*pageSize;
returnstudents.subList(firstIndex,lastIndex);
}

通过subList方法,获取到两个索引间的所有数据。最后在controller中创建测试方法:

@ResponseBody
@RequestMapping("/student/array/{currPage}/{pageSize}")
publicListgetStudentByArray(@PathVariable("currPage")intcurrPage,@PathVariable("pageSize")intpageSize){
Liststudent=StuServiceIml.queryStudentsByArray(currPage,pageSize);
returnstudent;
}

通过用户传入的currPage和pageSize获取指定数据。测试:首先我们来获取再没实现分页效果前获取到的所有数据,如下所示:接下来在浏览器输入http://localhost:8080/student/student/array/1/2测试实现了分页后的数据。获取第一页的数据,每页显示两条数据。结果如下: 输出的是指定的从第0-2条数据,可见我们通过数组分页的功能是成功的。(这里因为用到了关联查询,所以看起来数据可能比较多)缺点:数据库查询并返回所有的数据,而我们需要的只是极少数符合要求的数据。当数据量少时,还可以接受。当数据库数据量过大时,每次查询对数据库和程序的性能都会产生极大的影响。2、借助Sql语句进行分页在了解到通过数组分页的缺陷后,我们发现不能每次都对数据库中的所有数据都检索。然后在程序中对获取到的大量数据进行二次操作,这样对空间和性能都是极大的损耗。所以我们希望能直接在数据库语言中只检索符合条件的记录,不需要在通过程序对其作处理。这时,Sql语句分页技术横空出世。实现:通过sql语句实现分页也是非常简单的,只是需要改变我们查询的语句就能实现了,即在sql语句后面添加limit分页语句。首先还是在StudentMapper接口中添加sql语句查询的方法,如下:

ListqueryStudentsBySql(Mapdata);

然后在StudentMapper.xml文件中编写sql语句通过limiy关键字进行分页:

接下来还是在IStuService接口中定义方法,并且在StuServiceIml中对sql分页实现。

ListqueryStudentsBySql(intcurrPage,intpageSize);

@Override
publicListqueryStudentsBySql(intcurrPage,intpageSize){
Mapdata=newHashedMap();
data.put("currIndex",(currPage-1)*pageSize);
data.put("pageSize",pageSize);
returnstudentMapper.queryStudentsBySql(data);
}

sql分页语句如下:select * from table limit index, pageSize;所以在service中计算出currIndex:要开始查询的第一条记录的索引。测试:在浏览器输入http://localhost:8080/student/student/sql/1/2获取第一页的数据,每页显示两条数据。结果:从输出结果可以看出和数组分页的结果是一致的,因此sql语句的分页也是没问题的。缺点:虽然这里实现了按需查找,每次检索得到的是指定的数据。但是每次在分页的时候都需要去编写limit语句,很冗余。而且不方便统一管理,维护性较差。所以我们希望能够有一种更方便的分页实现。3、拦截器分页上面提到的数组分页和sql语句分页都不是我们今天讲解的重点,今天需要实现的是利用拦截器达到分页的效果。自定义拦截器实现了拦截所有以ByPage结尾的查询语句,并且利用获取到的分页相关参数统一在sql语句后面加上limit分页的相关语句,一劳永逸。不再需要在每个语句中单独去配置分页相关的参数了。。首先我们看一下拦截器的具体实现,在这里我们需要拦截所有以ByPage结尾的所有查询语句,因此要使用该拦截器实现分页功能,那么再定义名称的时候需要满足它拦截的规则(以ByPage结尾),如下所示:

packagecom.cbg.interceptor;

importorg.apache.ibatis.executor.Executor;
importorg.apache.ibatis.executor.parameter.ParameterHandler;
importorg.apache.ibatis.executor.resultset.ResultSetHandler;
importorg.apache.ibatis.executor.statement.StatementHandler;
importorg.apache.ibatis.mapping.MappedStatement;
importorg.apache.ibatis.plugin.*;
importorg.apache.ibatis.reflection.MetaObject;
importorg.apache.ibatis.reflection.SystemMetaObject;

importjava.sql.Connection;
importjava.util.Map;
importjava.util.Properties;

/**
*Createdbychenbogeon2017/5/7.
*

*Email:baigegechen@gmail.com*

*description:*//***@Intercepts说明是一个拦截器*@Signature拦截器的签名*type拦截的类型四大对象之一(Executor,ResultSetHandler,ParameterHandler,StatementHandler)*method拦截的方法*args参数*/@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class,Integer.class})})publicclassMyPageInterceptorimplementsInterceptor{//每页显示的条目数privateintpageSize;//当前现实的页数privateintcurrPage;privateStringdbType;@OverridepublicObjectintercept(Invocationinvocation)throwsThrowable{//获取StatementHandler,默认是RoutingStatementHandlerStatementHandlerstatementHandler=(StatementHandler)invocation.getTarget();//获取statementHandler包装类MetaObjectMetaObjectHandler=SystemMetaObject.forObject(statementHandler);//分离代理对象链while(MetaObjectHandler.hasGetter("h")){Objectobj=MetaObjectHandler.getValue("h");MetaObjectHandler=SystemMetaObject.forObject(obj);}while(MetaObjectHandler.hasGetter("target")){Objectobj=MetaObjectHandler.getValue("target");MetaObjectHandler=SystemMetaObject.forObject(obj);}//获取连接对象//Connectionconnection=(Connection)invocation.getArgs()[0];//object.getValue("delegate");获取StatementHandler的实现类//获取查询接口映射的相关信息MappedStatementmappedStatement=(MappedStatement)MetaObjectHandler.getValue("delegate.mappedStatement");StringmapId=mappedStatement.getId();//statementHandler.getBoundSql().getParameterObject();//拦截以.ByPage结尾的请求,分页功能的统一实现if(mapId.matches(".+ByPage$")){//获取进行数据库操作时管理参数的handlerParameterHandlerparameterHandler=(ParameterHandler)MetaObjectHandler.getValue("delegate.parameterHandler");//获取请求时的参数MapparaObject=(Map)parameterHandler.getParameterObject();//也可以这样获取//paraObject=(Map)statementHandler.getBoundSql().getParameterObject();//参数名称和在service中设置到map中的名称一致currPage=(int)paraObject.get("currPage");pageSize=(int)paraObject.get("pageSize");Stringsql=(String)MetaObjectHandler.getValue("delegate.boundSql.sql");//也可以通过statementHandler直接获取//sql=statementHandler.getBoundSql().getSql();//构建分页功能的sql语句StringlimitSql;sql=sql.trim();limitSql=sql+"limit"+(currPage-1)*pageSize+","+pageSize;//将构建完成的分页sql语句赋值个体'delegate.boundSql.sql',偷天换日MetaObjectHandler.setValue("delegate.boundSql.sql",limitSql);}//调用原对象的方法,进入责任链的下一级returninvocation.proceed();}//获取代理对象@OverridepublicObjectplugin(Objecto){//生成object对象的动态代理对象returnPlugin.wrap(o,this);}//设置代理对象的参数@OverridepublicvoidsetProperties(Propertiesproperties){//如果项目中分页的pageSize是统一的,也可以在这里统一配置和获取,这样就不用每次请求都传递pageSize参数了。参数是在配置拦截器时配置的。Stringlimit1=properties.getProperty("limit","10");this.pageSize=Integer.valueOf(limit1);this.dbType=properties.getProperty("dbType","mysql");}}


*Email:baigegechen@gmail.com
*
*description:
*/

/**
*@Intercepts说明是一个拦截器
*@Signature拦截器的签名
*type拦截的类型四大对象之一(Executor,ResultSetHandler,ParameterHandler,StatementHandler)
*method拦截的方法
*args参数
*/
@Intercepts({@Signature(type=StatementHandler.class,method=”prepare”,args={Connection.class,Integer.class})})
publicclassMyPageInterceptorimplementsInterceptor{

//每页显示的条目数
privateintpageSize;
//当前现实的页数
privateintcurrPage;

privateStringdbType;

@Override
publicObjectintercept(Invocationinvocation)throwsThrowable{
//获取StatementHandler,默认是RoutingStatementHandler
StatementHandlerstatementHandler=(StatementHandler)invocation.getTarget();
//获取statementHandler包装类
MetaObjectMetaObjectHandler=SystemMetaObject.forObject(statementHandler);

//分离代理对象链
while(MetaObjectHandler.hasGetter(“h”)){
Objectobj=MetaObjectHandler.getValue(“h”);
MetaObjectHandler=SystemMetaObject.forObject(obj);
}

while(MetaObjectHandler.hasGetter(“target”)){免费云主机域名
Objectobj=MetaObjectHandler.getValue(“target”);
MetaObjectHandler=SystemMetaObject.forObject(obj);
}

//获取连接对象
//Connectionconnection=(Connection)invocation.getArgs()[0];

//object.getValue(“delegate”);获取StatementHandler的实现类

//获取查询接口映射的相关信息
MappedStatementmappedStatement=(MappedStatement)MetaObjectHandler.getValue(“delegate.mappedStatement”);
StringmapId=mappedStatement.getId();

//statementHandler.getBoundSql().getParameterObject();

//拦截以.ByPage结尾的请求,分页功能的统一实现
if(mapId.matches(“.+ByPage$”)){
//获取进行数据库操作时管理参数的handler
ParameterHandlerparameterHandler=(ParameterHandler)MetaObjectHandler.getValue(“delegate.parameterHandler”);
//获取请求时的参数
MapparaObject=(Map)parameterHandler.getParameterObject();
//也可以这样获取
//paraObject=(Map)statementHandler.getBoundSql().getParameterObject();

//参数名称和在service中设置到map中的名称一致
currPage=(int)paraObject.get(“currPage”);
pageSize=(int)paraObject.get(“pageSize”);

Stringsql=(String)MetaObjectHandler.getValue(“delegate.boundSql.sql”);
//也可以通过statementHandler直接获取
//sql=statementHandler.getBoundSql().getSql();

//构建分页功能的sql语句
StringlimitSql;
sql=sql.trim();
limitSql=sql+”limit”+(currPage-1)*pageSize+”,”+pageSize;

//将构建完成的分页sql语句赋值个体’delegate.boundSql.sql’,偷天换日
MetaObjectHandler.setValue(“delegate.boundSql.sql”,limitSql);
}
//调用原对象的方法,进入责任链的下一级
returninvocation.proceed();
}

//获取代理对象
@Override
publicObjectplugin(Objecto){
//生成object对象的动态代理对象
returnPlugin.wrap(o,this);
}

//设置代理对象的参数
@Override
publicvoidsetProperties(Propertiesproperties){
//如果项目中分页的pageSize是统一的,也可以在这里统一配置和获取,这样就不用每次请求都传递pageSize参数了。参数是在配置拦截器时配置的。
Stringlimit1=properties.getProperty(“limit”,”10″);
this.pageSize=Integer.valueOf(limit1);
this.dbType=properties.getProperty(“dbType”,”mysql”);
}
}上面即是拦截器功能的实现,在intercept方法中获取到select标签和sql语句的相关信息,拦截所有以ByPage结尾的select查询,并且统一在查询语句后面添加limit分页的相关语句,统一实现分页功能。重点详解:StatementHandler是一个接口,而我们在代码中通过StatementHandler statementHandler = (StatementHandler) invocation.getTarget();获取到的是StatementHandler默认的实现类RoutingStatementHandler。而RoutingStatementHandler只是一个中间代理,他不会提供具体的方法。那你可能会纳闷了,拦截器中基本上是依赖statementHandler获取各种对象和属性的,没有具体属性和方法怎么行??接着看下面代码:

privatefinalStatementHandlerdelegate;

publicRoutingStatementHandler(Executorexecutor,MappedStatementms,Objectparameter,RowBoundsrowBounds,ResultHandlerresultHandler,BoundSqlboundSql){
switch(RoutingStatementHandler.SyntheticClass_1.$SwitchMap$org$apache$ibatis$mapping$StatementType[ms.getStatementType().ordinal()]){
case1:
this.delegate=newSimpleStatementHandler(executor,ms,parameter,rowBounds,resultHandler,boundSql);
break;
case2:
this.delegate=newPreparedStatementHandler(executor,ms,parameter,rowBounds,resultHandler,boundSql);
break;
case3:
this.delegate=newCallableStatementHandler(executor,ms,parameter,rowBounds,resultHandler,boundSql);
break;
default:
thrownewExecutorException("Unknownstatementtype:"+ms.getStatementType());
}

}

原来它是通过不同的MappedStatement创建不同的StatementHandler实现类对象处理不同的情况。这里的到的StatementHandler实现类才是真正服务的。看到这里,你可能就会明白MappedStatement mappedStatement = (MappedStatement) MetaObjectHandler.getValue("delegate.mappedStatement");中delegate的来源了吧。至于为什么要这么去获取,后面我们会说道。拿到statementHandler后,我们会通过MetaObject MetaObjectHandler = SystemMetaObject.forObject(statementHandler);去获取它的包装对象,通过包装对象去获取各种服务。MetaObject:mybatis的一个工具类,方便我们有效的读取或修改一些重要对象的属性。四大对象(ResultSetHandler,ParameterHandler,Executor和statementHandler)提供的公共方法很少,要想直接获取里面属性的值很困难,但是可以通过MetaObject利用一些技术(内部反射实现)很轻松的读取或修改里面的数据。接下来说说:MappedStatement mappedStatement = (MappedStatement) MetaObjectHandler.getValue("delegate.mappedStatement");上面提到为什么要这么去获取MappedStatement对象??在RoutingStatementHandler中delegate是私有的(private final StatementHandler delegate;),有没有共有的方法去获取。所以这里只有通过反射来获取啦。MappedStatement是保存了xxMapper.xml中一个sql语句节点的所有信息的包装类,可以通过它获取到节点中的所有信息。在示例中我们拿到了id值,也就是方法的名称,通过名称区拦截所有需要分页的请求。通过StatementHandler的包装类,不光能拿到MappedStatement,还可以拿到下面的数据:

publicabstractclassBaseStatementHandlerimplementsStatementHandler{
protectedfinalConfigurationconfiguration;
protectedfinalObjectFactoryobjectFactory;
protectedfinalTypeHandlerRegistrytypeHandlerRegistry;
protectedfinalResultSetHandlerresultSetHandler;
protectedfinalParameterHandlerparameterHandler;
protectedfinalExecutorexecutor;
protectedfinalMappedStatementmappedStatement;
protectedfinalRowBoundsrowBounds;
protectedBoundSqlboundSql;

protectedBaseStatementHandler(Executorexecutor,MappedStatementmappedStatement,ObjectparameterObject,RowBoundsrowBounds,ResultHandlerresultHandler,BoundSqlboundSql){
this.configuration=mappedStatement.getConfiguration();
this.executor=executor;
this.mappedStatement=mappedStatement;
this.rowBounds=rowBounds;
this.typeHandlerRegistry=this.configuration.getTypeHandlerRegistry();
this.objectFactory=this.configuration.getObjectFactory();
if(boundSql==null){
this.generateKeys(parameterObject);
boundSql=mappedStatement.getBoundSql(parameterObject);
}

this.boundSql=boundSql;
this.parameterHandler=this.configuration.newParameterHandler(mappedStatement,parameterObject,boundSql);
this.resultSetHandler=this.configuration.newResultSetHandler(executor,mappedStatement,rowBounds,this.parameterHandler,resultHandler,boundSql);
}

上面的所有数据都可以通过反射拿到。几个重要的参数:Configuration:所有配置的相关信息。ResultSetHandler:用于拦截执行结果的组装。ParameterHandler:拦截执行Sql的参数的组装。Executor:执行Sql的全过程,包括组装参数、组装结果和执行Sql的过程。BoundSql:执行的Sql的相关信息。接下来我们通过如下代码拿到请求时的map对象(反射)。

//获取进行数据库操作时管理参数的handler
ParameterHandlerparameterHandler=(ParameterHandler)MetaObjectHandler.getValue("delegate.parameterHandler");
//获取请求时的参数
MapparaObject=(Map)parameterHandler.getParameterObject();
//也可以这样获取
//paraObject=(Map)statementHandler.getBoundSql().getParameterObject();

拿到我们需要的currPage和pageSize参数后,就是组装分页查询的sql语句’limitSql‘了。最后通过MetaObjectHandler.setValue("delegate.boundSql.sql", limitSql);将原始的sql语句替换成我们新的分页语句,完成偷天换日的功能,接下来让代码继续执行。编写好拦截器后,需要注册到项目中,才能发挥它的作用。在mybatis的配置文件中,添加如下代码:






如上所示,还能在里面配置一些属性,在拦截器的setProperties方法中可以获取配置好的属性值。如项目分页的pageSize参数的值固定,我们就可以配置在这里了,以后就不需要每次传入pageSize了,读取方式如下:

//读取配置的代理对象的参数
@Override
publicvoidsetProperties(Propertiesproperties){
Stringlimit1=properties.getProperty("limit","10");
this.pageSize=Integer.valueOf(limit1);
this.dbType=properties.getProperty("dbType","mysql");
}

到这里,有关拦截器的相关知识就讲解的差不多了,接下来就需要测试,是否我们这样写真的有效??首先还是添加dao层的方法和xml文件的sql语句配置,注意项目中拦截的是以ByPage结尾的请求,所以在这里,我们的方法名称也以此结尾:

方法
ListqueryStudentsByPage(Mapdata);

xml文件的select语句

可以看出,这里我们就不需要再去手动配置分页语句了。接下来是service层的接口编写和实现方法:

方法:
ListqueryStudentsByPage(intcurrPage,intpageSize);

实现:
@Override
publicListqueryStudentsByPage(intcurrPage,intpageSize){
Mapdata=newHashedMap();
data.put("currPage",currPage);
data.put("pageSize",pageSize);
returnstudentMapper.queryStudentsByPage(data);
}

这里我们虽然传入了currPage和pageSize两个参数,但是在sql的xml文件中并没有使用,直接在拦截器中获取到统一使用。最后编写controller的测试代码:

@ResponseBody
@RequestMapping("/student/page/{currPage}/{pageSize}")
publicListgetStudentByPage(@PathVariable("currPage")intcurrPage,@PathVariable("pageSize")intpageSize){
Liststudent=StuServiceIml.queryStudentsByPage(currPage,pageSize);
returnstudent;
}

测试:在浏览器输入:http://localhost:8080/student/student/page/1/2结果:
可见和上面两种分页的效果是一样的。4、RowBounds实现分页原理:通过RowBounds实现分页和通过数组方式分页原理差不多,都是一次获取所有符合条件的数据,然后在内存中对大数据进行操作,实现分页效果。只是数组分页需要我们自己去实现分页逻辑,这里更加简化而已。存在问题:一次性从数据库获取的数据可能会很多,对内存的消耗很大,可能导师性能变差,甚至引发内存溢出。适用场景:在数据量很大的情况下,建议还是适用拦截器实现分页效果。RowBounds建议在数据量相对较小的情况下使用。简单介绍:这是代码实现上最简单的一种分页方式,只需要在dao层接口中要实现分页的方法中加入RowBounds参数,然后在service层通过offset(从第几行开始读取数据,默认值为0)和limit(要显示的记录条数,默认为java允许的最大整数:2147483647)两个参数构建出RowBounds对象,在调用dao层方法的时,将构造好的RowBounds传进去就能轻松实现分页效果了。具体操作如下:dao层接口方法:

//加入RowBounds参数
publicListqueryUsersByPage(StringuserName,RowBoundsrowBounds);

然后在service层构建RowBounds,调用dao层方法:

@Override
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.SUPPORTS)
publicListqueryRolesByPage(StringroleName,intstart,intlimit){
returnroleDao.queryRolesByPage(roleName,newRowBounds(start,limit));
}

RowBounds就是一个封装了offset和limit简单类,如下所示:

publicclassRowBounds{
publicstaticfinalintNO_ROW_OFFSET=0;
publicstaticfinalintNO_ROW_LIMIT=2147483647;
publicstaticfinalRowBoundsDEFAULT=newRowBounds();
privateintoffset;
privateintlimit;

publicRowBounds(){
this.offset=0;
this.limit=2147483647;
}

publicRowBounds(intoffset,intlimit){
this.offset=offset;
this.limit=limit;
}

publicintgetOffset(){
returnthis.offset;
}

publicintgetLimit(){
returnthis.limit;
}
}

只需要这两步操作,就能轻松实现分页效果了,是不是很神奇。但却不简单,内部是怎么实现的??给大家提供一个简单的思路:RowBounds分页简单原理结论:从上面四种sql分页的实现方式可以看出,通过RowBounds实现是最简便的,但是通过拦截器的实现方式是最优的方案。只需一次编写,所有的分页方法共同使用,还可以避免多次配置时的出错机率,需要修改时也只需要修改这一个文件,一劳永逸。而且是我们自己实现的,便于我们去控制和增加一些逻辑处理,使我们在外层更简单的使用。同时也不会出现数组分页和RowBounds分页导致的性能问题。当然,具体情况可以采取不同的解决方案。数据量小时,RowBounds不失为一种好办法。但是数据量大时,实现拦截器就很有必要了。到此,相信大家对“mybatis分页的方式有哪些”有了更深的了解,不妨来实际操作一番吧!这里是百云主机网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

相关推荐: php的tcp粘包和拆包怎么实现

今天小编给大家分享一下php的tcp粘包和拆包怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。tcp 长链接模式下,使用固定消息头长度的方…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

Like (0)
Donate 微信扫一扫 微信扫一扫
Previous 03/05 17:09
Next 03/05 19:24

相关推荐