spring+mybatis 物理分页

新项目用的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;
	}
}

3:mybatis接口

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分,各位童鞋见谅了~

  • 32
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 25
    评论
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值