代码实现
分页插件实现
package com. example. plugin. page. request;
import lombok. Data;
@Data
public class PageParams {
private Integer page;
private Integer pageSize;
private Boolean useFlag;
private Boolean checkFlag;
private Boolean cleanOrderBy;
private Integer total;
private Integer totalPage;
}
package com. example. plugin. page;
import com. example. plugin. page. request. PageParams;
import org. apache. ibatis. executor. parameter. ParameterHandler;
import org. apache. ibatis. executor. statement. StatementHandler;
import org. apache. ibatis. mapping. BoundSql;
import org. apache. ibatis. mapping. MappedStatement;
import org. apache. ibatis. plugin. *;
import org. apache. ibatis. reflection. MetaObject;
import org. apache. ibatis. reflection. SystemMetaObject;
import org. apache. ibatis. scripting. defaults. DefaultParameterHandler;
import org. apache. ibatis. session. Configuration;
import java. beans. IntrospectionException;
import java. beans. PropertyDescriptor;
import java. lang. reflect. Field;
import java. lang. reflect. InvocationTargetException;
import java. lang. reflect. Method;
import java. sql. Connection;
import java. sql. PreparedStatement;
import java. sql. ResultSet;
import java. sql. SQLException;
import java. util. Map;
import java. util. Properties;
@Intercepts ( {
@Signature ( type = StatementHandler. class ,
method = "prepare" ,
args = { Connection. class , Integer. class } )
} )
public class PagePlugin implements Interceptor {
private Integer defaultPage;
private Integer defaultPageSize;
private Boolean defaultUseFlag;
private Boolean defaultCheckFlag;
private Boolean defaultCleanOrderBy;
@Override
public Object intercept ( Invocation invocation) throws Throwable {
StatementHandler statementHandler = ( StatementHandler) getUnProxyObject ( invocation. getTarget ( ) ) ;
MetaObject metaStatementHandler = SystemMetaObject. forObject ( statementHandler) ;
String sql = ( String) metaStatementHandler. getValue ( "delegate.boundSql.sql" ) ;
if ( ! checkSelect ( sql) ) {
return invocation. proceed ( ) ;
}
BoundSql boundSql = ( BoundSql) metaStatementHandler. getValue ( "delegate.boundSql" ) ;
Object parameterObject = boundSql. getParameterObject ( ) ;
PageParams pageParams = getPageParams ( parameterObject) ;
if ( pageParams == null) {
return invocation. proceed ( ) ;
}
Boolean useFlag = pageParams. getUseFlag ( ) == null ? this . defaultUseFlag : pageParams. getUseFlag ( ) ;
if ( ! useFlag) {
return invocation. proceed ( ) ;
}
Integer page = pageParams. getPage ( ) == null ? this . defaultPage : pageParams. getPage ( ) ;
Integer pageSize = pageParams. getPageSize ( ) == null ? this . defaultPageSize : pageParams. getPageSize ( ) ;
Boolean checkFlag = pageParams. getCheckFlag ( ) == null ? this . defaultCheckFlag : pageParams. getCheckFlag ( ) ;
Boolean cleanOrderBy = pageParams. getCleanOrderBy ( ) == null ? this . defaultCleanOrderBy : pageParams. getCleanOrderBy ( ) ;
int total = getTotal ( invocation, metaStatementHandler, boundSql, cleanOrderBy) ;
pageParams. setTotal ( total) ;
int totalPage = total % pageSize == 0 ? total / pageSize : total / pageSize + 1 ;
pageParams. setTotalPage ( totalPage) ;
checkPage ( checkFlag, page, pageSize) ;
return preparedSQL ( invocation, metaStatementHandler, boundSql, page, pageSize) ;
}
private Object getUnProxyObject ( Object target) {
MetaObject metaStatementHandler = SystemMetaObject. forObject ( target) ;
Object object = null;
while ( metaStatementHandler. hasGetter ( "h" ) ) {
object = metaStatementHandler. getValue ( "h" ) ;
metaStatementHandler = SystemMetaObject. forObject ( object) ;
}
if ( object == null) {
return target;
}
return object;
}
private boolean checkSelect ( String sql) {
String trimSql = sql. trim ( ) ;
int index = trimSql. toLowerCase ( ) . indexOf ( "select" ) ;
return index == 0 ;
}
private PageParams getPageParams ( Object parameterObject) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
PageParams pageParams = null;
if ( parameterObject == null) {
return pageParams;
}
if ( parameterObject instanceof Map ) {
Map< String, Object> paramMap = ( Map< String, Object> ) parameterObject;
for ( Map. Entry< String, Object> entry : paramMap. entrySet ( ) ) {
if ( entry. getValue ( ) instanceof PageParams ) {
return ( PageParams) entry. getValue ( ) ;
}
}
} else if ( parameterObject instanceof PageParams ) {
return ( PageParams) parameterObject;
} else {
Field[ ] fields = parameterObject. getClass ( ) . getDeclaredFields ( ) ;
for ( Field field : fields) {
if ( field. getType ( ) == PageParams. class ) {
PropertyDescriptor propertyDescriptor = new PropertyDescriptor ( field. getName ( ) , field. getClass ( ) ) ;
Method readMethod = propertyDescriptor. getReadMethod ( ) ;
return ( PageParams) readMethod. invoke ( parameterObject) ;
}
}
}
return pageParams;
}
private int getTotal ( Invocation invocation, MetaObject metaStatementHandler, BoundSql oldBoundSql, Boolean cleanOrderBy) throws SQLException {
MappedStatement oldMappedStatement = ( MappedStatement) metaStatementHandler. getValue ( "delegate.mappedStatement" ) ;
Configuration configuration = oldMappedStatement. getConfiguration ( ) ;
String sql = ( String) metaStatementHandler. getValue ( "delegate.boundSql.sql" ) ;
if ( cleanOrderBy) {
sql = this . cleanOrderByForsql ( sql) ;
}
String countSql = String. format ( "select count(*) as total from (%s) $_paging" , sql) ;
Connection connection = ( Connection) invocation. getArgs ( ) [ 0 ] ;
PreparedStatement countPreparedStatement = null;
int total = 0 ;
try {
countPreparedStatement = connection. prepareStatement ( countSql) ;
BoundSql countBoundSql = new BoundSql ( configuration, countSql, oldBoundSql. getParameterMappings ( ) , oldBoundSql. getParameterObject ( ) ) ;
ParameterHandler parameterHandler = new DefaultParameterHandler ( oldMappedStatement, oldBoundSql. getParameterObject ( ) , countBoundSql) ;
parameterHandler. setParameters ( countPreparedStatement) ;
ResultSet resultSet = countPreparedStatement. executeQuery ( ) ;
while ( resultSet. next ( ) ) {
total = resultSet. getInt ( "total" ) ;
}
} finally {
if ( countPreparedStatement != null) {
countPreparedStatement. close ( ) ;
}
}
return total;
}
private void checkPage ( Boolean checkFlag, Integer pageNum, Integer pageTotal) {
if ( checkFlag) {
if ( pageNum > pageTotal) {
throw new RuntimeException ( String. format ( "查询失败, 查询页码[%d]大于总页数[%d]" , pageNum, pageTotal) ) ;
}
}
}
private Object preparedSQL ( Invocation invocation, MetaObject metaStatementHandler, BoundSql boundSql, int pageNum, int pageSize) throws InvocationTargetException, IllegalAccessException, SQLException {
String sql = boundSql. getSql ( ) ;
String newSql = String. format ( "select * from (%s) $_paging_table limit ?, ?" , sql) ;
metaStatementHandler. setValue ( "delegate.boundSql.sql" , newSql) ;
Object statementObj = invocation. proceed ( ) ;
this . preparePageDataParams ( ( PreparedStatement) statementObj, pageNum, pageSize) ;
return statementObj;
}
private void preparePageDataParams ( PreparedStatement ps, int pageNum, int pageSize) throws SQLException {
int parameterCount = ps. getParameterMetaData ( ) . getParameterCount ( ) ;
ps. setInt ( parameterCount - 1 , ( pageNum - 1 ) ) ;
ps. setInt ( parameterCount, pageSize) ;
}
private String cleanOrderByForsql ( String sql) {
String newSql = sql. toLowerCase ( ) ;
int index = newSql. lastIndexOf ( "order" ) ;
if ( index == - 1 ) {
return newSql;
}
return newSql. substring ( 0 , index) ;
}
@Override
public Object plugin ( Object target) {
return Plugin. wrap ( target, this ) ;
}
@Override
public void setProperties ( Properties properties) {
String strDefaultPage = properties. getProperty ( "default.page" , "1" ) ;
String strDefaultPageSize = properties. getProperty ( "default.pageSize" , "20" ) ;
String strDefaultUseFlag = properties. getProperty ( "default.useFlag" , "true" ) ;
String strDefaultCheckFlag = properties. getProperty ( "default.checkFlag" , "false" ) ;
String strDefaultCleanOrderBy = properties. getProperty ( "default.cleanOrderBy" , "true" ) ;
defaultPage = Integer. parseInt ( strDefaultPage) ;
defaultPageSize = Integer. parseInt ( strDefaultPageSize) ;
defaultUseFlag = Boolean. parseBoolean ( strDefaultUseFlag) ;
defaultCheckFlag = Boolean. parseBoolean ( strDefaultCheckFlag) ;
defaultCleanOrderBy = Boolean. parseBoolean ( strDefaultCleanOrderBy) ;
}
}
注册分页插件
package com. example. plugin. page. config;
import com. example. plugin. page. PagePlugin;
import org. apache. ibatis. session. Configuration;
import org. mybatis. spring. annotation. MapperScan;
import org. mybatis. spring. boot. autoconfigure. ConfigurationCustomizer;
import org. springframework. context. annotation. Bean;
import java. util. Properties;
@org . springframework. context. annotation. Configuration
@MapperScan ( { "com.example.plugin.page.mapper" } )
public class MapperConfig {
@Bean
public ConfigurationCustomizer configurationCustomizer ( ) {
return new ConfigurationCustomizer ( ) {
@Override
public void customize ( Configuration configuration) {
PagePlugin pagePlugin = new PagePlugin ( ) ;
Properties properties = new Properties ( ) ;
properties. setProperty ( "default.page" , "1" ) ;
properties. setProperty ( "default.pageSize" , "20" ) ;
properties. setProperty ( "default.useFlag" , "true" ) ;
properties. setProperty ( "default.checkFlag" , "false" ) ;
properties. setProperty ( "default.cleanOrderBy" , "true" ) ;
pagePlugin. setProperties ( properties) ;
configuration. addInterceptor ( pagePlugin) ;
}
} ;
}
}
实现业务代码
public interface Table1Mapper {
List< Table1> getList ( GetListRequest request) ;
}
< ? xml version= "1.0" encoding= "UTF-8" ? >
< ! DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
< mapper namespace= "com.example.plugin.page.mapper.Table1Mapper" >
< select id= "getList" resultType= "com.example.plugin.page.entity.Table1" >
select *
from table1
where
id >= #{ minId}
order by id
< / select>
< / mapper>
配置
spring. datasource. driver- class - name= com. mysql. jdbc. Driver
spring. datasource. url= jdbc: mysql: / / localhost: 3306 / test
spring. datasource. username= root
spring. datasource. password= 123456
mybatis. type- aliases- package = com. example. plugin. page. entity
mybatis. mapperLocations= classpath: mappers
测试
测试代码
package com. example. plugin. page;
import com. alibaba. fastjson. JSON;
import com. example. plugin. page. entity. Table1;
import com. example. plugin. page. mapper. Table1Mapper;
import com. example. plugin. page. request. GetListRequest;
import com. example. plugin. page. response. GetListResponse;
import org. junit. Test;
import org. junit. runner. RunWith;
import org. springframework. beans. BeanUtils;
import org. springframework. beans. factory. annotation. Autowired;
import org. springframework. boot. test. context. SpringBootTest;
import org. springframework. test. context. junit4. SpringRunner;
import java. util. List;
@RunWith ( SpringRunner. class )
@SpringBootTest
public class PagePluginTest {
@Autowired
private Table1Mapper table1Mapper;
@Test
public void test1 ( ) {
GetListRequest request = new GetListRequest ( ) ;
request. setPage ( 1 ) ;
request. setPageSize ( 2 ) ;
request. setMinId ( 2 ) ;
request. setCleanOrderBy ( true ) ;
List< Table1> list = table1Mapper. getList ( request) ;
GetListResponse< Table1> getListResponse = new GetListResponse ( ) ;
getListResponse. setList ( list) ;
BeanUtils. copyProperties ( request, getListResponse) ;
System. out. println ( JSON. toJSONString ( getListResponse) ) ;
}
}
效果
2020 - 07 - 19 21 : 30 : 09.313 WARN 11932 -- - [ l- 1 housekeeper] com. zaxxer. hikari. pool. HikariPool : HikariPool- 1 - Thread starvation or clock leap detected ( housekeeper delta= 1 m18s456ms611µs800ns) .
== > Preparing: select count ( * ) as total from ( select * from table1 where id >= ? ) $_paging
2020 - 07 - 19 21 : 39 : 34.632 WARN 11932 -- - [ l- 1 housekeeper] com. zaxxer. hikari. pool. HikariPool : HikariPool- 1 - Thread starvation or clock leap detected ( housekeeper delta= 9 m28s34ms475µs600ns) .
== > Parameters: 2 ( Integer)
<= = Columns: total
<= = Row: 5
<= = Total: 1
2020 - 07 - 19 21 : 40 : 30.668 WARN 11932 -- - [ l- 1 housekeeper] com. zaxxer. hikari. pool. HikariPool : HikariPool- 1 - Thread starvation or clock leap detected ( housekeeper delta= 56 s35ms593µs700ns) .
== > Preparing: select * from ( select * from table1 where id >= ? order by id) $_paging_table limit ? , ?
== > Parameters: 0 ( Integer) , 2 ( Integer) , 2 ( Integer)
<= = Columns: id, name
<= = Row: 2 , name2
<= = Row: 3 , name3
<= = Total: 2
Closing non transactional SqlSession [ org. apache. ibatis. session. defaults. DefaultSqlSession@191a0351 ]
{ "list" : [ { "id" : 2 , "name" : "name2" } , { "id" : 3 , "name" : "name3" } ] , "page" : 1 , "pageSize" : 2 , "total" : 5 , "totalPage" : 3 }