新项目用的spring mvc 和 mybatis 分页。研究了一下,集众家之长然后形成现在的项目。我把分页部分剥离出来与大家分享。如有不妥的地方欢迎交流拍砖。
单独做了一个小项目,放到了下载区,如果有用到的朋友可以去下载。
本项目采用了拦截器,就是mybaits自带的plus功能。将每次select操作都进行拦截。
项目架构如下:
1:首先从cotroller层出发,啥也不说,上代码。这个最实惠
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层代码。没啥可说的上代码
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.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; } }简要说明:刚刚忘记了,有一些查询操作是不需要分页的。可以自行修改拦截器中代码部分,加个判断啥的。
5:spring 配置
<bean id="paginationInterceptor" class="com.flydreamer.interceptor.PaginationInterceptor"></bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="plugins"> <ref bean="paginationInterceptor"/> </property> </bean>
大致就是这样了。完整的可运行项目已放到这里。最近CSDN没有分了,收1分,各位童鞋见谅了~