spring+mybatis 物理分页
Posted wangpeng047
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring+mybatis 物理分页相关的知识,希望对你有一定的参考价值。
新项目用的spring mvc 和 mybatis 分页。研究了一下,集众家之长然后形成现在的项目。我把分页部分剥离出来与大家分享。如有不妥的地方欢迎交流拍砖。
单独做了一个小项目,放到了下载区,如果有用到的朋友可以去下载。
本项目采用了拦截器,就是mybaits自带的plus功能。将每次select操作都进行拦截。
项目架构如下:
1:首先从cotroller层出发,啥也不说,上代码。这个最实惠
[java] view plain copy print ?- package com.flydreamer.controller;
- import java.util.HashMap;
- import java.util.Map;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.servlet.ModelAndView;
- import com.flydreamer.logic.UserService;
- import com.flydreamer.page.Page;
- import com.flydreamer.page.PageContext;
- import com.flydreamer.util.web.HandlerResult;
- @Controller
- @RequestMapping("/user.do")
- public class UserController
- @Autowired
- private UserService userService;
- @RequestMapping(params = ("method=list"))
- public ModelAndView listAll(HttpServletRequest request,HttpServletResponse response)
- //可以将分页参数获取封装,已达到更好的复用效果。
- //page=2&pageSize=10&totalPages=19&totalRows=188
- String pagec = request.getParameter("page");
- String pageSize = request.getParameter("pageSize");
- String totalPages = request.getParameter("totalPages");
- String totalRows = request.getParameter("totalRows");
- //方法1:将分页参数直接放到mapper接口函数参数中,也可在对象中定义名字为page的属性,反射一样可以得到
- //后台连接直接获取
- //Page page = new Page();
- //方法2:不用进行map传参,用ThreadLocal进行传参,方便没有侵入性
- PageContext page = PageContext.getContext();
- //请自行验证
- if(null == pagec)
- page.setCurrentPage(1);
- page.setPageSize(10);
- else
- page.setCurrentPage(Integer.parseInt(pagec));
- page.setPageSize(Integer.parseInt(pageSize));
- page.setTotalPages(Integer.parseInt(totalPages));
- page.setTotalRows(Integer.parseInt(totalRows));
- page.setPagination(true);
- // 方法1用
- // Map map = new HashMap();
- // map.put("page", page);
- // HandlerResult rs = userService.list(map);
- //方法2用
- HandlerResult rs = userService.list();
- ModelAndView mv = new ModelAndView("/views/show.jsp");
- mv.addObject("userList", rs.getResultObj());
- mv.addObject("page",page);
- return mv;
package com.flydreamer.controller;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.flydreamer.logic.UserService;
import com.flydreamer.page.Page;
import com.flydreamer.page.PageContext;
import com.flydreamer.util.web.HandlerResult;
@Controller
@RequestMapping("/user.do")
public class UserController
@Autowired
private UserService userService;
@RequestMapping(params = ("method=list"))
public ModelAndView listAll(HttpServletRequest request,HttpServletResponse response)
//可以将分页参数获取封装,已达到更好的复用效果。
//page=2&pageSize=10&totalPages=19&totalRows=188
String pagec = request.getParameter("page");
String pageSize = request.getParameter("pageSize");
String totalPages = request.getParameter("totalPages");
String totalRows = request.getParameter("totalRows");
//方法1:将分页参数直接放到mapper接口函数参数中,也可在对象中定义名字为page的属性,反射一样可以得到
//后台连接直接获取
//Page page = new Page();
//方法2:不用进行map传参,用ThreadLocal进行传参,方便没有侵入性
PageContext page = PageContext.getContext();
//请自行验证
if(null == pagec)
page.setCurrentPage(1);
page.setPageSize(10);
else
page.setCurrentPage(Integer.parseInt(pagec));
page.setPageSize(Integer.parseInt(pageSize));
page.setTotalPages(Integer.parseInt(totalPages));
page.setTotalRows(Integer.parseInt(totalRows));
page.setPagination(true);
// 方法1用
// Map map = new HashMap();
// map.put("page", page);
// HandlerResult rs = userService.list(map);
//方法2用
HandlerResult rs = userService.list();
ModelAndView mv = new ModelAndView("/views/show.jsp");
mv.addObject("userList", rs.getResultObj());
mv.addObject("page",page);
return mv;
简要说明:本文采用两种方式将page对象传入到拦截器中。第一种方式是采用参数传值,不管是用map还是在统一参数对象中名称为page的属性都可以在分页拦截器中得到page的值。第二种方式是用ThreadLocal,对service层没有侵入性。比较方便。
2:Service层代码。没啥可说的上代码
[java] view plain copy print ?- package com.flydreamer.logic;
- import java.util.Map;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import com.flydreamer.orm.mapper.UsersMapper;
- import com.flydreamer.util.web.HandlerResult;
- @Service
- public class UserService
- @Autowired
- private UsersMapper usersMappser;
- /**
- * 统一Service出口,方便管理
- * @param map
- * @return
- */
- public HandlerResult list(Map map)
- HandlerResult rs = new HandlerResult();
- rs.setResultObj(usersMappser.list(map));
- return rs;
- /**
- * 采用本地线程的方式分页
- * @return
- */
- public HandlerResult list()
- HandlerResult rs = new HandlerResult();
- rs.setResultObj(usersMappser.list2());
- return rs;
- public UsersMapper getUsersMappser()
- return usersMappser;
- public void setUsersMappser(UsersMapper usersMappser)
- this.usersMappser = usersMappser;
package com.flydreamer.logic;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.flydreamer.orm.mapper.UsersMapper;
import com.flydreamer.util.web.HandlerResult;
@Service
public class UserService
@Autowired
private UsersMapper usersMappser;
/**
* 统一Service出口,方便管理
* @param map
* @return
*/
public HandlerResult list(Map map)
HandlerResult rs = new HandlerResult();
rs.setResultObj(usersMappser.list(map));
return rs;
/**
* 采用本地线程的方式分页
* @return
*/
public HandlerResult list()
HandlerResult rs = new HandlerResult();
rs.setResultObj(usersMappser.list2());
return rs;
public UsersMapper getUsersMappser()
return usersMappser;
public void setUsersMappser(UsersMapper usersMappser)
this.usersMappser = usersMappser;
3:mybatis接口
[java] view plain copy print ?
- package com.flydreamer.orm.mapper;
- import java.util.List;
- import java.util.Map;
- import com.flydreamer.orm.SqlMapper;
- public interface UsersMapper extends SqlMapper
- public List list(Map para);
- public List list2();
package com.flydreamer.orm.mapper;
import java.util.List;
import java.util.Map;
import com.flydreamer.orm.SqlMapper;
public interface UsersMapper extends SqlMapper
public List list(Map para);
public List list2();
4:page的拦截器
- package com.flydreamer.interceptor;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.util.List;
- import java.util.Properties;
- import org.apache.ibatis.builder.xml.dynamic.ForEachSqlNode;
- import org.apache.ibatis.executor.ErrorContext;
- import org.apache.ibatis.executor.Executor;
- import org.apache.ibatis.executor.ExecutorException;
- import org.apache.ibatis.logging.Log;
- import org.apache.ibatis.logging.LogFactory;
- import org.apache.ibatis.mapping.BoundSql;
- import org.apache.ibatis.mapping.MappedStatement;
- import org.apache.ibatis.mapping.ParameterMapping;
- import org.apache.ibatis.mapping.ParameterMode;
- import org.apache.ibatis.mapping.SqlSource;
- import org.apache.ibatis.mapping.MappedStatement.Builder;
- import org.apache.ibatis.plugin.Interceptor;
- import org.apache.ibatis.plugin.Intercepts;
- import org.apache.ibatis.plugin.Invocation;
- import org.apache.ibatis.plugin.Plugin;
- import org.apache.ibatis.plugin.Signature;
- import org.apache.ibatis.reflection.MetaObject;
- import org.apache.ibatis.reflection.property.PropertyTokenizer;
- import org.apache.ibatis.session.Configuration;
- import org.apache.ibatis.session.ResultHandler;
- import org.apache.ibatis.session.RowBounds;
- import org.apache.ibatis.type.TypeHandler;
- import org.apache.ibatis.type.TypeHandlerRegistry;
- import com.flydreamer.page.Dialect;
- import com.flydreamer.page.mysql5Dialect;
- import com.flydreamer.page.OracleDialect;
- import com.flydreamer.page.Page;
- import com.flydreamer.page.PageContext;
- import com.flydreamer.page.ReflectHelper;
- //只拦截select部分
- @Intercepts(@Signature(type=Executor.class,method="query",args= MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class ))
- public class PaginationInterceptor implements Interceptor
- private final static Log log = LogFactory.getLog(PaginationInterceptor.class);
- Dialect dialect = new MySql5Dialect();
- public Object intercept(Invocation invocation) throws Throwable
- MappedStatement mappedStatement=(MappedStatement)invocation.getArgs()[0];
- Object parameter = invocation.getArgs()[1];
- BoundSql boundSql = mappedStatement.getBoundSql(parameter);
- String originalSql = boundSql.getSql().trim();
- RowBounds rowBounds = (RowBounds)invocation.getArgs()[2];
- Object parameterObject = boundSql.getParameterObject();
- if(boundSql==null || boundSql.getSql()==null || "".equals(boundSql.getSql()))
- return null;
- //分页参数--上下文传参
- Page page = null;
- PageContext context=PageContext.getContext();
- //map传参每次都将currentPage重置,先判读map再判断context
- if(parameterObject!=null)
- page = (Page)ReflectHelper.isPage(parameterObject,"page");
- //分页参数--context参数里的Page传参
- if(page==null && context.isPagination()==true)
- page = context;
- //后面用到了context的东东
- if(page!=null && page.isPagination()==true)
- int totpage=page.getTotalRows();
- //得到总记录数
- if (totpage==0)
- StringBuffer countSql = new StringBuffer(originalSql.length()+100 );
- countSql.append("select count(1) from (").append(originalSql).append(") t");
- Connection connection=mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection() ;
- PreparedStatement countStmt = connection.prepareStatement(countSql.toString());
- BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(),countSql.toString(),boundSql.getParameterMappings(),parameterObject);
- setParameters(countStmt,mappedStatement,countBS,parameterObject);
- ResultSet rs = countStmt.executeQuery();
- if (rs.next())
- totpage = rs.getInt(1);
- rs.close();
- countStmt.close();
- connection.close();
- //分页计算
- page.init(totpage,page.getPageSize(),page.getCurrentPage());
- if(rowBounds == null || rowBounds == RowBounds.DEFAULT)
- rowBounds= new RowBounds(page.getPageSize()*(page.getCurrentPage()-1),page.getPageSize());
- //分页查询 本地化对象 修改数据库注意修改实现
- String pagesql=dialect.getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit());
- invocation.getArgs()[2] = new RowBounds(RowBounds.NO_ROW_OFFSET, RowBounds.NO_ROW_LIMIT);
- BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), pagesql,boundSql.getParameterMappings(),boundSql.getParameterObject());
- MappedStatement newMs = copyFromMappedStatement(mappedStatement,new BoundSqlSqlSource(newBoundSql));
- invocation.getArgs()[0]= newMs;
- return invocation.proceed();
- public static class BoundSqlSqlSource implements SqlSource
- BoundSql boundSql;
- public BoundSqlSqlSource(BoundSql boundSql)
- this.boundSql = boundSql;
- public BoundSql getBoundSql(Object parameterObject)
- return boundSql;
- public Object plugin(Object arg0)
- return Plugin.wrap(arg0, this);
- public void setProperties(Properties arg0)
- /**
- * 对SQL参数(?)设值,参考org.apache.ibatis.executor.parameter.DefaultParameterHandler
- * @param ps
- * @param mappedStatement
- * @param boundSql
- * @param parameterObject
- * @throws SQLException
- */
- private void setParameters(PreparedStatement ps,MappedStatement mappedStatement,BoundSql boundSql,Object parameterObject) throws SQLException
- ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
- List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
- if (parameterMappings != null)
- Configuration configuration = mappedStatement.getConfiguration();
- TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
- MetaObject metaObject = parameterObject == null ? null: configuration.newMetaObject(parameterObject);
- for (int i = 0; i < parameterMappings.size(); i++)
- ParameterMapping parameterMapping = parameterMappings.get(i);
- if (parameterMapping.getMode() != ParameterMode.OUT)
- Object value;
- String propertyName = parameterMapping.getProperty();
- PropertyTokenizer prop = new PropertyTokenizer(propertyName);
- if (parameterObject == null)
- value = null;
- else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass()))
- value = parameterObject;
- else if (boundSql.hasAdditionalParameter(propertyName))
- value = boundSql.getAdditionalParameter(propertyName);
- else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)&& boundSql.hasAdditionalParameter(prop.getName()))
- value = boundSql.getAdditionalParameter(prop.getName());
- if (value != null)
- value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));
- else
- value = metaObject == null ? null : metaObject.getValue(propertyName);
- TypeHandler typeHandler = parameterMapping.getTypeHandler();
- if (typeHandler == null)
- throw new ExecutorException("There was no TypeHandler found for parameter "+ propertyName + " of statement "+ mappedStatement.getId());
- typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
- private MappedStatement copyFromMappedStatement(MappedStatement ms,
- SqlSource newSqlSource)
- Builder builder = new MappedStatement.Builder(ms.getConfiguration(),
- ms.getId(), newSqlSource, ms.getSqlCommandType());
- builder.resource(ms.getResource());
- builder.fetchSize(ms.getFetchSize());
- builder.statementType(ms.getStatementType());
- builder.keyGenerator(ms.getKeyGenerator());
- builder.keyProperty(ms.getKeyProperty());
- builder.timeout(ms.getTimeout());
- builder.parameterMap(ms.getParameterMap());
- builder.resultMaps(ms.getResultMaps());
- builder.cache(ms.getCache());
- MappedStatement newMs = builder.build();
- return newMs;
package com.flydreamer.interceptor;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;
import org.apache.ibatis.builder.xml.dynamic.ForEachSqlNode;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.MappedStatement.Builder;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import com.flydreamer.page.Dialect;
import com.flydreamer.page.MySql5Dialect;
import com.flydreamer.page.OracleDialect;
import com.flydreamer.page.Page;
import com.flydreamer.page.PageContext;
import com.flydreamer.page.ReflectHelper;
//只拦截select部分
@Intercepts(@Signature(type=Executor.class,method="query",args= MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class ))
public class PaginationInterceptor implements Interceptor
private final static Log log = LogFactory.getLog(PaginationInterceptor.class);
Dialect dialect = new MySql5Dialect();
public Object intercept(Invocation invocation) throws Throwable
MappedStatement mappedStatement=(MappedStatement)invocation.getArgs()[0];
Object parameter = invocation.getArgs()[1];
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
String originalSql = boundSql.getSql().trim();
RowBounds rowBounds = (RowBounds)invocation.getArgs()[2];
Object parameterObject = boundSql.getParameterObject();
if(boundSql==null || boundSql.getSql()==null || "".equals(boundSql.getSql()))
return null;
//分页参数--上下文传参
Page page = null;
PageContext context=PageContext.getContext();
//map传参每次都将currentPage重置,先判读map再判断context
if(parameterObject!=null)
page = (Page)ReflectHelper.isPage(parameterObject,"page");
//分页参数--context参数里的Page传参
if(page==null && context.isPagination()==true)
page = context;
//后面用到了context的东东
if(page!=null && page.isPagination()==true)
int totpage=page.getTotalRows();
//得到总记录数
if (totpage==0)
StringBuffer countSql = new StringBuffer(originalSql.length()+100 );
countSql.append("select count(1) from (").append(originalSql).append(") t");
Connection connection=mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection() ;
PreparedStatement countStmt = connection.prepareStatement(countSql.toString());
BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(),countSql.toString(),boundSql.getParameterMappings(),parameterObject);
setParameters(countStmt,mappedStatement,countBS,parameterObject);
ResultSet rs = countStmt.executeQuery();
if (rs.next())
totpage = rs.getInt(1);
rs.close();
countStmt.close();
connection.close();
//分页计算
page.init(totpage,page.getPageSize(),page.getCurrentPage());
if(rowBounds == null || rowBounds == RowBounds.DEFAULT)
rowBounds= new RowBounds(page.getPageSize()*(page.getCurrentPage()-1),page.getPageSize());
//分页查询 本地化对象 修改数据库注意修改实现
String pagesql=dialect.getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit());
invocation.getArgs()[2] = new RowBounds(RowBounds.NO_ROW_OFFSET, RowBounds.NO_ROW_LIMIT);
BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), pagesql,boundSql.getParameterMappings(),boundSql.getParameterObject());
MappedStatement newMs = copyFromMappedStatement(mappedStatement,new BoundSqlSqlSource(newBoundSql));
invocation.getArgs()[0]= newMs;
return invocation.proceed();
public static class BoundSqlSqlSource implements SqlSource
BoundSql boundSql;
public BoundSqlSqlSource(BoundSql boundSql)
this.boundSql = boundSql;
public BoundSql getBoundSql(Object parameterObject)
return boundSql;
public Object plugin(Object arg0)
return Plugin.wrap(arg0, this);
public void setProperties(Properties arg0)
/**
* 对SQL参数(?)设值,参考org.apache.ibatis.executor.parameter.DefaultParameterHandler
* @param ps
* @param mappedStatement
* @param boundSql
* @param parameterObject
* @throws SQLException
*/
private void setParameters(PreparedStatement ps,MappedStatement mappedStatement,BoundSql boundSql,Object parameterObject) throws SQLException
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null)
Configuration configuration = mappedStatement.getConfiguration();
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
MetaObject metaObject = parameterObject == null ? null: configuration.newMetaObject(parameterObject);
for (int i = 0; i < parameterMappings.size(); i++)
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT)
Object value;
String propertyName = parameterMapping.getProperty();
PropertyTokenizer prop = new PropertyTokenizer(propertyName);
if (parameterObject == null)
value = null;
else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass()))
value = parameterObject;
else if (boundSql.hasAdditionalParameter(propertyName))
value = boundSql.getAdditionalParameter(propertyName);
else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)&& boundSql.hasAdditionalParameter(prop.getName()))
value = boundSql.getAdditionalParameter(prop.getName());
if (value != null)
value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));
else
value = metaObject == null ? null : metaObject.getValue(propertyName);
TypeHandler typeHandler = parameterMapping.getTypeHandler();
if (typeHandler == null)
throw new ExecutorException("There was no TypeHandler found for parameter "+ propertyName + " of statement "+ mappedStatement.getId());
typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
private MappedStatement copyFromMappedStatement(MappedStatement ms,
SqlSource newSqlSource)
Builder builder = new MappedStatement.Builder(ms.getConfiguration(),
ms.getId(), newSqlSource, ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
builder.keyProperty(ms.getKeyProperty());
builder.timeout(ms.getTimeout());
builder.parameterMap(ms.getParameterMap());
builder.resultMaps(ms.getResultMaps());
builder.cache(ms.getCache());
MappedStatement newMs = builder.build();
return newMs;
简要说明:刚刚忘记了,有一些查询操作是不需要分页的。可以自行修改拦截器中代码部分,加个判断啥的。
5:spring 配置
[html] view plain copy print ?- <bean id="paginationInterceptor" class="com.flydreamer.interceptor.PaginationInterceptor"></bean>
<bean id="paginationInterceptor" class="com.flydreamer.interceptor.PaginationInterceptor"></bean>
[html] view plain copy print ?
- <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
- <property name="dataSource" ref="dataSource" />
- <property name="plugins">
- <ref bean="paginationInterceptor"/>
- </property>
- </bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="plugins">
<ref bean="paginationInterceptor"/>
</property>
</bean>
大致就是这样了。完整的可运行项目已放到这里。最近CSDN没有分了,收1分,各位童鞋见谅了~
以上是关于spring+mybatis 物理分页的主要内容,如果未能解决你的问题,请参考以下文章