mybatis中的分页并且带了排序效果

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">今天做了一下测试,测试发现分页中存在一些问题:</span>

分页中的排序效果没有显示出来。首先看下排序中的一些注意的事项:

1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order
by "111", 如果传入的值是id,则解析成的sql为order by "id".

2. $将传入的数据直接显示生成在sql中。如:order by $user_id$,如果传入的值是111,那么解析成sql时的值为order by user_id,  如果传入的值是id,则解析成的sql为order by id.

3. #方式能够很大程度防止sql注入。

4.$方式无法防止Sql注入。

5.$方式一般用于传入数据库对象,例如传入表名.

6.一般能用#的就别用$.

MyBatis排序时使用order by 动态参数时需要注意,用$而不是#

字符串替换

默认情况下,使用#{}格式的语法会导致MyBatis创建预处理语句属性并以它为背景设置安全的值(比如?)。这样做很安全,很迅速也是首选做法,有时你只是想直接在SQL语句中插入一个不改变的字符串。比如,像ORDER BY,你可以这样来使用:

ORDER BY ${columnName}

这里MyBatis不会修改或转义字符串。

重要:接受从用户输出的内容并提供给语句中不变的字符串,这样做是不安全的。这会导致潜在的SQL注入攻击,因此你不应该允许用户输入这些字段,或者通常自行转义并检查。

下面的分页是一个关于贴吧的分页处理代码:

TopicController.java

package com.mogu.controller;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import cn.jpush.api.utils.StringUtils;

import com.mogu.common.StatusCode;
import com.mogu.exception.TopicException;
import com.mogu.model.MoguData;
import com.mogu.model.MoguPageData;
import com.mogu.model.Topic;
import com.mogu.service.topic.ITopicService;
import com.mogu.util.Base64Handler;
import com.mogu.util.LoadOfBASE64;
import com.mogu.util.ObjectUtils;
import com.mogu.util.page.PageControlData;

/**
 * 是发表文章的Controller类 ClassName:TopicController <br/>
 * Date: 2015年3月21日下午3:48:00 <br/>
 *
 * @author 永文
 * @version
 * @see
 */
@Controller
@RequestMapping("/topic")
public class TopicController {
	Logger logger = Logger.getLogger(Topic.class);
	@Resource
	ITopicService topicService;

	@RequestMapping("/publish")
	public @ResponseBody MoguData<Topic> publish(
			@RequestBody MoguData<Topic> params) {
		MoguData<Topic> moguData = new MoguData<Topic>();
		Topic topic = params.getData();
		try {
			if (!StringUtils.isEmpty(topic.getContent())) {
				topic.setContent(URLDecoder.decode(topic.getContent(), "UTF-8"));
			}
			topic.setPosttime(new Date());
			// 对图片进行处理
			if (!StringUtils.isEmpty(topic.getImgs())) {
				String[] imgs = topic.getImgs().split(",");
				StringBuffer sb = new StringBuffer();
				for (int i = 0; i < imgs.length; i++) {
					if (!StringUtils.isEmpty(imgs[i])) {
						String imgUrl = Base64Handler
								.getInstance()
								.convertBase64DataToImg(imgs[i],
										"fileBbsImagePath", "SqlBbsImagePath");
						sb.append(imgUrl + ",");
					}
				}
				topic.setImgs(sb.toString());
			}
			int record = topicService.saveTopic(topic);
			if (record > 0) {// 插入成功
				moguData.setStatuscode(StatusCode.SUCCESS.value());
				moguData.setMessage(StatusCode.errorMsg(StatusCode.SUCCESS
						.value()));
			}
		} catch (TopicException e) {
			moguData.setStatuscode(StatusCode.PUBLISH_FAIL.value());
			moguData.setMessage(StatusCode.errorMsg(StatusCode.PUBLISH_FAIL
					.value()));
			return moguData;
		} catch (UnsupportedEncodingException e) {
			moguData.setStatuscode(StatusCode.PUBLISH_FAIL.value());
			moguData.setMessage(StatusCode.errorMsg(StatusCode.PUBLISH_FAIL
					.value()));
			return moguData;
		} catch (IOException e) {
			moguData.setStatuscode(StatusCode.PUBLISH_FAIL.value());
			moguData.setMessage(StatusCode.errorMsg(StatusCode.PUBLISH_FAIL
					.value()));
			return moguData;
		}
		return moguData;
	}

	/**
	 * 通过页码、文章类型等来获取数据 Function: TODO. <br/>
	 * Date: 2015年3月23日上午10:42:10 <br/>
	 *
	 * @author 永文
	 * @version
	 * @see
	 */
	@RequestMapping("/getTopicByPage")
	public @ResponseBody MoguPageData<List<Topic>> getTopicByPage(
			@RequestBody MoguPageData<Topic> params) {
		MoguPageData<List<Topic>> moguPageData = new MoguPageData<List<Topic>>();
		PageControlData<Topic> po = new PageControlData<Topic>();
		Topic ppo = params.getData();
		po.setPageSize(params.getPerPageCount());
		po.setCurrentPage(params.getCurrentPage());
		PageControlData<Topic> pageControlData = null;
		try {
			pageControlData = topicService.getTopicByPage(ppo, po);
		} catch (TopicException e) {
			e.printStackTrace();
			moguPageData.setMessage(StatusCode
					.errorMsg(StatusCode.GAIN_PAGING_FAIL.value()));
			moguPageData.setStatuscode(StatusCode.GAIN_PAGING_FAIL.value());
			return moguPageData;
		}
		moguPageData.setData(pageControlData.getResultList());
		moguPageData.setStatuscode(StatusCode.SUCCESS.value());
		moguPageData
				.setMessage(StatusCode.errorMsg(StatusCode.SUCCESS.value()));
		return moguPageData;

	}
}

TopicServiceImpl.java

package com.mogu.service.topic.impl;

import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mogu.exception.TopicException;
import com.mogu.mapper.TopicMapper;
import com.mogu.model.Topic;
import com.mogu.model.User;
import com.mogu.service.topic.ITopicService;
import com.mogu.util.LoadOfBASE64;
import com.mogu.util.ObjectUtils;
import com.mogu.util.UrlUtil;
import com.mogu.util.page.PageControlData;
import com.mogu.util.page.PageParam;

@Service
public class TopicServiceImpl implements ITopicService {

	Logger logger = Logger.getLogger(TopicServiceImpl.class);

	@Resource
	TopicMapper topicMapper;

	/**
	 * 确保查询出记录同时更新记录的浏览次数的记录行
	 */
	@Override
	@Transactional
	public PageControlData<Topic> getTopicByPage(Topic ppo,
			PageControlData<Topic> po) throws TopicException {
		PageParam<Topic> pageParam = new PageParam<Topic>(ppo, po);
		List<Topic> list = null;
		try {
			list = topicMapper.pageQuery(pageParam);
			if(list.size() == 0){
				return po;
			}
			int updateRecord = topicMapper.updateByTopics(list);
			if(list.size() != updateRecord){//说明没有同步更新浏览次数
				logger.info("贴吧记录更新失败");
				throw new TopicException("贴吧记录更新失败");
			}
		} catch (SQLException e) {
			e.printStackTrace();
			logger.info("贴吧记录获取失败"+e.getMessage());
			throw new TopicException("贴吧记录获取失败"+e.getMessage());
		}
		Iterator<Topic> iterator = list.iterator();
		Topic topic = null;
		User user = null;
		UrlUtil urlUtil = UrlUtil.getInstance();
		while(iterator.hasNext()){
			topic  = iterator.next();
			String string = topic.getImgs();
			StringBuffer sb= null;
			if(ObjectUtils.validateString(string)){
				sb = new StringBuffer();
				String[] strArr = string.split(",");
				for(int i = 0 ; i < strArr.length; i ++){
					if(ObjectUtils.validateString(strArr[i])){
						if(i ==strArr.length-1)
							sb.append(urlUtil .getAbsoluteUrl(strArr[i]));
						else
							sb.append(urlUtil .getAbsoluteUrl(strArr[i])+",");
					}
				}
				topic.setImgs(sb.toString());
			}
			//对用户图像进行处理
			user = topic.getUser();
			user.setImg(urlUtil.getAbsoluteUrl(user.getImg()));
		}
		po.setResultList(list);
		return po;
	}

	@Override
	public int saveTopic(Topic topic) throws TopicException  {
		try {
			return topicMapper.insert(topic);
		} catch (SQLException e) {
			e.printStackTrace();
			throw new TopicException("发表贴吧失败!"+e.getMessage());
		}
	}
}

关于page相关的类,上面业务代码。

PageControlData.java

package com.mogu.util.page;

import java.io.Serializable;
import java.util.List;

/**
 * 版权所有:@copy; 2004 ZTE Corporation.版权所有.
 * 文件编号:M00_PageControlData.java
 * 文件名称:PageControlData.java
 * 系统编号:Z0001001
 * 系统名称:市场营销管理系统(系统用户)
 * 模块编号:M02
 * 模块名称:项目管理
 * 设计文件:M02_PU02项目管理设计模型.cat,M02_PU02项目管理用例设计.cat
 * 完成日期:
 * 作        者:
 * 内容摘要:项目管理基础页面类
 */
public class PageControlData<T> implements Serializable{

	private static final long serialVersionUID = 1L;

	//默认页面尺寸
	private static final int DEFULT_PAGE_SIZE = 10;

	/**
	 * 原始的结果的列表
	 */
	private List<T> resultList;

	/**
	 * 页面总数
	 */
	private int pageCount;

	/**
	 * 当前页面序号
	 */
	private int currentPage;

	/**
	 * 总记录条数
	 */
	private int resultCount;

	/**
	 * 页面尺寸
	 */
	private int pageSize = DEFULT_PAGE_SIZE;

	/**
	 * 跳转到的页面序号
	 */
	private int changePageNumber;

	/**
	 * 起始的记录序号
	 */
	private int startRowNum;

	/**
	 * 终了记录序号
	 */
	private int endRowNum;

	/**
	 * 描述:用于排序的对象
	 */
	private SortData sort;

	public PageControlData() {

	}

	/**
	 * 方法名称: init
	 * 内容摘要: 初始化分页对象
	 */
	public void init()
	{
		//初始化页面总数
		pageCount = 0;
		//初始化当前页面序号
		currentPage = 0;
		//初始化总记录条数
		resultCount = 0;
		//初始化页面尺寸
		pageSize = DEFULT_PAGE_SIZE;
	}

	/**
	 * Access method for the resultList property.
	 *
	 * @return   the current value of the resultList property
	 */
	public List<T> getResultList()
	{
		return resultList;
	}

	/**
	 * Sets the value of the resultList property.
	 *
	 * @param aResultList the new value of the resultList property
	 */
	public void setResultList(List<T> aResultList)
	{
		resultList = aResultList;
	}

	/**
	 * Access method for the pageCount property.
	 *
	 * @return   the current value of the pageCount property
	 */
	public int getPageCount()
	{
		//判断记录总数是否能整除页尺寸
		if (resultCount % pageSize == 0)
		{
			//整除则直接取整相除
			pageCount = (resultCount / pageSize);
		}
		else
		{
			//否则取整相除后加一
			pageCount = (resultCount / pageSize) + 1;
		}
		return pageCount;
	}

	/**
	 * Sets the value of the pageCount property.
	 *
	 * @param aPageCount the new value of the pageCount property
	 */
	public void setPageCount(int aPageCount)
	{
		pageCount = aPageCount;
	}

	/**
	 * Access method for the currentPage property.
	 *
	 * @return   the current value of the currentPage property
	 */
	public int getCurrentPage()
	{
		// 判断总记录数大于零且当前也是小于一的情况
		if (currentPage < 1 && resultCount > 0)
		{
				currentPage = 1;
		}
		//判断当前页序号是否溢出
		if (currentPage > getPageCount())
		{
				currentPage = pageCount;
		}
		return currentPage;
	}

	/**
	 * Sets the value of the currentPage property.
	 *
	 * @param aCurrentPage the new value of the currentPage property
	 */
	public void setCurrentPage(int aCurrentPage)
	{
		//设置当前页序号、小于零的情况忽略
		if(aCurrentPage >= 0)
		{
			currentPage = aCurrentPage;
		}
	}

	/**
	 * Access method for the resultCount property.
	 *
	 * @return   the current value of the resultCount property
	 */
	public int getResultCount()
	{
		return resultCount;
	}

	/**
	 * Sets the value of the resultCount property.
	 *
	 * @param aResultCount the new value of the resultCount property
	 */
	public void setResultCount(int aResultCount)
	{
		//设置总记录条数
		resultCount = aResultCount;
	}

	/**
	 * Access method for the pageSize property.
	 *
	 * @return   the current value of the pageSize property
	 */
	public int getPageSize()
	{
		return pageSize;
	}

	/**
	 * Sets the value of the pageSize property.
	 *
	 * @param aPageSize the new value of the pageSize property
	 */
	public void setPageSize(int aPageSize)
	{
		pageSize = aPageSize;

	}

	/**
	 * Access method for the changePageNumber property.
	 *
	 * @return   the current value of the changePageNumber property
	 */
	public int getChangePageNumber()
	{
		return changePageNumber;
	}

	/**
	 * Sets the value of the changePageNumber property.
	 *
	 * @param aChangePageNumber the new value of the changePageNumber property
	 */
	public void setChangePageNumber(int aChangePageNumber)
	{
		//设置跳转到的页面序号
		changePageNumber = aChangePageNumber;
		//设置当前页序号
		setCurrentPage(changePageNumber);
	}

	/**
	 * Determines if the isFirstPage property is true.
	 *
	 * @return   <code>true<code> if the isFirstPage property is true
	 */
	public boolean getIsFirstPage()
	{
		return currentPage <= 1 ? true : false;
	}

	/**
	 * Determines if the isLastPage property is true.
	 *
	 * @return   <code>true<code> if the isLastPage property is true
	 */
	public boolean getIsLastPage()
	{
		return pageCount <= currentPage ? true : false;
	}

	/**
	 * Access method for the startRowNum property.
	 *
	 * @return   the current value of the startRowNum property
	 */
	public int getStartRowNum()
	{
		//判断记录总数是否能整除页尺寸
		if (currentPage > getPageCount())
		{
			currentPage = pageCount;
		}
		return ((currentPage - 1) * pageSize > 0 ? (currentPage - 1) * pageSize : 0);
	}

	/**
	 * Access method for the endRowNum property.
	 *
	 * @return   the current value of the endRowNum property
	 */
	public int getEndRowNum()
	{
		//判断记录总数是否能整除页尺寸
		if (currentPage > getPageCount())
		{
			currentPage = pageCount;
		}
		//如果当前页小于一则结束序号为页面大小,否则按公式计算
		return (currentPage - 1) > 0 ? (currentPage - 1) * pageSize + pageSize : pageSize;
	}

	/**
	 * Sets the value of the startRowNum property.
	 *
	 * @param aStartRowNum the new value of the startRowNum property
	 */
	public void setStartRowNum(int aStartRowNum)
	{
			startRowNum = aStartRowNum;
	}

	/**
	 * Sets the value of the endRowNum property.
	 *
	 * @param aEndRowNum the new value of the endRowNum property
	 */
	public void setEndRowNum(int aEndRowNum)
	{
			endRowNum = aEndRowNum;
	}

	//获取当前页面记录数
	public int getPageDataCount(){
		if(resultList != null)
			return resultList.size();
		else
			return 0;
	}

	public SortData getSort() {
		return sort;
	}

	public void setSort(SortData sort) {
		this.sort = sort;
	}
}

PageParam.java

/**
 *
 */
package com.mogu.util.page;

import java.util.HashMap;

/**
 * @author chenh
 * @date 2013年9月3日
 */
@SuppressWarnings("serial")
public class PageParam<T> extends HashMap<String,Object>{

	private static final String KEY_PO = "po";
	private static final String KEY_PAGE = "page";

	public PageParam(){
		super();
	}

	public PageParam(Object t,PageControlData<T> page){
		this.put(KEY_PO,t);
		this.put(KEY_PAGE, page);
	}

	@SuppressWarnings("unchecked")
	public T getParamObject(){
		return (T)this.get(KEY_PO);
	}

	@SuppressWarnings("unchecked")
	public PageControlData<T> getPage(){
		return (PageControlData<T>)this.get(KEY_PAGE);
	}
}

MysqlPageHandler.java 该类实现了PageHandler接口

/**
 *
 */
package com.mogu.util.page;

/**
 * @author chenh
 * @date 2013年9月5日
 */
public class MysqlPageHandler implements PageHandler{

	public String handlerCountSql(String sql) {
		return "select count(*) from ("+sql+") as total";
	}

	public String handlerPageSql(String sql) {
		return sql+" limit ?,?";
	}

}

OrclcePageHander.java该类也实现了PageHandler接口

/**
 *
 */
package com.mogu.util.page;

/**
 * @author chenh
 * @date 2013年9月3日
 */
public class OraclePageHandler implements PageHandler{

	public String handlerCountSql(String sql) {
		return "SELECT COUNT(*) FROM ("+sql+")";
	}

	public String handlerPageSql(String sql) {
		return "SELECT * FROM "
				+ "(WITH RESULTTABLE AS ( "+sql+") SELECT ROWNUM AS ROW_NUM,R.* FROM RESULTTABLE R) PAGERESULT "
				+ "WHERE PAGERESULT.ROW_NUM >? AND PAGERESULT.ROW_NUM <=?";
	}
}

PageInterceptor.java

/**
 *
 */
package com.mogu.util.page;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMapping.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.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.apache.log4j.Logger;

/**
 * @author chenh
 * @date 2013年9月2日
 */
@Intercepts({@Signature(type= StatementHandler.class,method = "prepare",args = {Connection.class})})
@SuppressWarnings("unchecked")
public class PageInterceptor implements Interceptor{

	private PageHandler pageHandler;
	public static final Logger logger = Logger.getLogger(PageInterceptor.class);

	public Object intercept(Invocation invocation) throws Throwable {
		RoutingStatementHandler statementHandler = (RoutingStatementHandler)invocation.getTarget();
		BoundSql boundSql = statementHandler.getBoundSql();
		Object param = boundSql.getParameterObject();
		if(param instanceof PageParam){
			logger.debug("page query");
			PageParam<Object> pageParam =(PageParam<Object>)param;
			PageControlData<Object> page = pageParam.getPage();
			//ReflectUtil.setFieldValue(boundSql, "parameterObject", pageParam.getParamObject());
			Connection connection = (Connection)invocation.getArgs()[0];
			String sql = boundSql.getSql();
			String sqlCount = pageHandler.handlerCountSql(sql);
			logger.debug("sqlCount: "+sqlCount);
			//通过反射获取到当前RoutingStatementHandler对象的delegate属性
			StatementHandler delegate = (StatementHandler)ReflectUtil.getFieldValue(statementHandler, "delegate");
			//通过反射获取delegate父类BaseStatementHandler的mappedStatement属性
			MappedStatement mappedStatement = (MappedStatement)ReflectUtil.getFieldValue(delegate, "mappedStatement");
			//通过BoundSql获取对应的参数映射
		    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
		    //利用Configuration、查询记录数的Sql语句countSql、参数映射关系parameterMappings和参数对象page建立查询记录数对应的BoundSql对象。
		    BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), sqlCount, parameterMappings, param);
		    //通过mappedStatement、参数对象page和BoundSql对象countBoundSql建立一个用于设定参数的ParameterHandler对象
		    ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, param, countBoundSql);  

			PreparedStatement pstmt = null;
			ResultSet rs = null;
			try {
				pstmt = connection.prepareStatement(sqlCount);
				parameterHandler.setParameters(pstmt);
				// 之后就是执行获取总记录数的Sql语句和获取结果了。
				rs = pstmt.executeQuery();
				if (rs.next()) {
					int totalRecord = rs.getInt(1);
					logger.debug("page query count: "+totalRecord);
					page.setResultCount(totalRecord);
				}
			} catch (SQLException e) {
				logger.error(e.getMessage());
				e.printStackTrace();
			} finally {
				try {
					if (rs != null)
						rs.close();
					if (pstmt != null)
						pstmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			Configuration configuration = (Configuration)ReflectUtil.getFieldValue(delegate, "configuration");
			if(!(parameterMappings instanceof ArrayList)){
				parameterMappings = new ArrayList<ParameterMapping>();
				ReflectUtil.setFieldValue(boundSql, "parameterMappings", parameterMappings);
			}
			parameterMappings.add(new Builder(configuration, "page.startRowNum", Object.class).build());
			//parameterMappings.add(new Builder(configuration, "page.endRowNum", Object.class).build());
			parameterMappings.add(new Builder(configuration, "page.pageSize", Object.class).build());
			String sqlPage = pageHandler.handlerPageSql(sql);
			logger.debug("sqlPage: "+sqlPage);
			//利用反射设置当前BoundSql对应的sql属性为我们建立好的分页Sql语句
			ReflectUtil.setFieldValue(boundSql, "sql", sqlPage);
		}
		return invocation.proceed();
	}

	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	public void setProperties(Properties properties) {
		String pageHandlerClass = (String)properties.get("pageHandler");
		logger.info("use page handler: "+pageHandlerClass);
		try {
			pageHandler = (PageHandler)Class.forName(pageHandlerClass).newInstance();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			logger.error(e.getMessage());
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			logger.error(e.getMessage());
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			logger.error(e.getMessage());
		}
	}

}

sortData.java

package com.mogu.util.page;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import org.springframework.util.StringUtils;

public class SortData implements Iterable<SortData.OrderData>,Serializable{

	private static final long serialVersionUID = 1L;

	public static final Direction DEFAULT_DIRECTION = Direction.DESC;

	private final List<OrderData> orders;

	/**
	 * @param orders 不能是 null。
	 */
	public SortData(OrderData... orders) {
		this(Arrays.asList(orders));
	}

	/**
	 * @param orders 不能是 null,也不能包含 null。
	 */
	public SortData(List<OrderData> orders) {
		if (null == orders || orders.isEmpty()) {
			throw new IllegalArgumentException("你必须提供至少一个排序属性!");
		}

		this.orders = orders;
	}

	/**
	 * @param properties 不能是 null,也不能包含 null。
	 */
	public SortData(String... properties) {
		this(DEFAULT_DIRECTION, properties);
	}

	/**
	 * @param direction 当direction为null时,默认为DEFAULT_DIRECTION
	 * @param properties 不能是 null,也不能包含 null或空串
	 */
	public SortData(Direction direction, String... properties) {
		this(direction, properties == null ? new ArrayList<String>() : Arrays.asList(properties));
	}

	/**
	 * @param direction
	 * @param properties
	 */
	public SortData(Direction direction, List<String> properties) {
		if (properties == null || properties.isEmpty()) {
			throw new IllegalArgumentException("你必须提供至少一个排序属性!");
		}

		this.orders = new ArrayList<OrderData>(properties.size());

		for (String property : properties) {
			this.orders.add(new OrderData(direction, property));
		}
	}

	/**
	 * 返回由当前Sort与给定sort取并集后的新的Sort。
	 *
	 * @param sort 可以为null
	 * @return
	 */
	public SortData and(SortData sort) {
		if (sort == null) {
			return this;
		}

		ArrayList<OrderData> these = new ArrayList<OrderData>(this.orders);

		for (OrderData order : sort) {
			these.add(order);
		}

		return new SortData(these);
	}

	/**
	 * 返回给定property的Order对象
	 *
	 * @param property
	 * @return
	 */
	public OrderData getOrderFor(String property) {
		for (OrderData order : this) {
			if (order.getProperty().equals(property)) {
				return order;
			}
		}

		return null;
	}

	/*
	 * (non-Javadoc)
	 * @see java.lang.Iterable#iterator()
	 */
	public Iterator<OrderData> iterator() {
		return this.orders.iterator();
	}

	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}

		if (!(obj instanceof SortData)) {
			return false;
		}

		SortData that = (SortData) obj;

		return this.orders.equals(that.orders);
	}

	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		int result = 17;
		result = 31 * result + orders.hashCode();
		return result;
	}

	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return StringUtils.collectionToCommaDelimitedString(orders);
	}

	/**
	 * 排序方向的枚举类型
	 */
	public static enum Direction {
		ASC, DESC;

		/**
		 * 根据给定值返回相应枚举实例。
		 *
		 * @param value
		 * @return
		 */
		public static Direction fromString(String value) {
			try {
				return Direction.valueOf(value.toUpperCase(Locale.US));
			} catch (Exception e) {
				throw new IllegalArgumentException(String.format("非法的值 '%s' ! 合法的值只能是 “desc”或“asc”(大小写不敏感)。", value), e);
			}
		}
	}

	/**
	 * 排序属性
	 */
	public static class OrderData implements Serializable {
		private static final long serialVersionUID = 1L;

		private final Direction direction;
		private final String property;

		/**
		 * @param direction 当direction是null时,默认为DEFAULT_DIRECTION
		 * @param property 不能是null或空串
		 */
		public OrderData(Direction direction, String property) {
			if (!StringUtils.hasText(property)) {
				throw new IllegalArgumentException("属性不能是null或空串!");
			}

			this.direction = direction == null ? DEFAULT_DIRECTION : direction;
			this.property = property;
		}

		/**
		 * @param property 不能是null或空串
		 */
		public OrderData(String property) {
			this(DEFAULT_DIRECTION, property);
		}

		/**
		 * 获得当前属性的排序方向。
		 *
		 * @return
		 */
		public Direction getDirection() {
			return direction;
		}

		/**
		 * 获得当前排序属性的字符串表示。
		 *
		 * @return
		 */
		public String getProperty() {
			return property;
		}

		/**
		 * 返回当前排序属性是否是增序排序。
		 *
		 * @return
		 */
		public boolean isAscending() {
			return this.direction.equals(Direction.ASC);
		}

		/**
		 * 根据给定的Direction返回一个新的Order.
		 *
		 * @param order
		 * @return
		 */
		public OrderData with(Direction order) {
			return new OrderData(order, this.property);
		}

		/**
		 * 根据给定的properties返回一个新的Sort
		 *
		 * @param properties
		 * @return
		 */
		public SortData withProperties(String... properties) {
			return new SortData(this.direction, properties);
		}

		/*
		 * (non-Javadoc)
		 * @see java.lang.Object#hashCode()
		 */
		@Override
		public int hashCode() {
			int result = 17;

			result = 31 * result + direction.hashCode();
			result = 31 * result + property.hashCode();

			return result;
		}

		/*
		 * (non-Javadoc)
		 * @see java.lang.Object#equals(java.lang.Object)
		 */
		@Override
		public boolean equals(Object obj) {
			if (this == obj) {
				return true;
			}

			if (!(obj instanceof OrderData)) {
				return false;
			}

			OrderData that = (OrderData) obj;

			return this.direction.equals(that.direction) && this.property.equals(that.property);
		}

		/*
		 * (non-Javadoc)
		 * @see java.lang.Object#toString()
		 */
		@Override
		public String toString() {
			return String.format("%s: %s", property, direction);
		}
	}

	public static void main(String[] args) {
		SortData sort = new SortData("abc", "kkk", "III");
		System.out.println(sort);
	}

}

TopicMapper.xml

  <resultMap type="com.mogu.model.Topic" id="ExtendBaseResultMap">
      <association property="user" column="userid" select="selectUser" javaType="com.mogu.model.User"></association>
  </resultMap>

  <sql id="Base_Column_List" >
    id, title, content, imgs, type, userId, posttime, ipAddr, location, phoneType, commentCount,
    browseCount, praiseCount, parentId, status, create_time, create_user, update_time,
    update_user
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select
    <include refid="Base_Column_List" />
    from mogu_user_topic
    where id = #{id,jdbcType=INTEGER}
  </select>

  <!-- 分页查询 -->
	<select id="pageQuery" resultMap="ExtendBaseResultMap">
		SELECT * FROM mogu_user_topic
		<where>
			<if test="po.createTime != null">
				AND create_time=#{bo.createTime,jdbcType=TIMESTAMP}
			</if>
			<if test="po.updateTime != null">
				AND update_time=#{bo.updateTime,jdbcType=TIMESTAMP}
			</if>
	   </where>
		 <if test="page.sort != null ">
		        <foreach collection="page.sort" index="index" item="order">
		            ORDER BY #{order.property,jdbcType=VARCHAR} #{order.direction,jdbcType=VARCHAR}
		        </foreach>
		</if>
	</select>

	<select id="selectUser" resultType="com.mogu.model.User" parameterType="java.lang.Integer">
	    SELECT * FROM mogu_user
	    WHERE id = #{id,jdbcType=INTEGER}
	</select>

在上面的代码中存在这样一个问题希望大家注意:

1.种

 <if test="page.sort != null ">
		        <foreach collection="page.sort" index="index" item="order">            <pre name="code" class="java">                              ORDER BY #{order.property,jdbcType=VARCHAR} #{order.direction,jdbcType=VARCHAR}

</foreach></if>


<pre name="code" class="java"> <if test="page.sort != null ">
		        <foreach collection="page.sort" index="index" item="order">            <pre name="code" class="java">                              ORDER BY #{order.property} #{order.direction}

</foreach></if>



上面的都是无效的,需要使用下面的方式来处理排序问题

2.种

	 <if test="page.sort != null ">
		        <foreach collection="page.sort" index="index" item="order">
		            ORDER BY ${order.property} ${order.direction}
		        </foreach>
		</if>

上面的在查询分页记录的时候不会出现问题 ,但是前期有个COUNT操作,会出现问题下面的问题:

2015-04-01 17:00:07,736 [main] DEBUG org.mybatis.spring.SqlSessionUtils       -Creating a new SqlSession
2015-04-01 17:00:07,743 [main] DEBUG org.mybatis.spring.SqlSessionUtils       -Registering transaction synchronization for SqlSession [[email protected]]
2015-04-01 17:00:07,843 [main] DEBUG ing.transaction.SpringManagedTransaction -JDBC Connection [jdbc:mysql://120.24.245.201:3306/mogubrothers?useUnicode=true&characterEncoding=utf8, [email protected], MySQL-AB JDBC Driver] will be managed by Spring
2015-04-01 17:00:07,850 [main] DEBUG com.mogu.mapper.TopicMapper.pageQuery    -ooo Using Connection [jdbc:mysql://120.24.245.201:3306/mogubrothers?useUnicode=true&characterEncoding=utf8, [email protected], MySQL-AB JDBC Driver]
2015-04-01 17:00:07,857 [main] DEBUG com.mogu.util.page.PageInterceptor       -page query
2015-04-01 17:00:07,857 [main] DEBUG com.mogu.util.page.PageInterceptor       -sqlCount: select count(*) from (SELECT * FROM mogu_user_topic

		            ORDER BY ? ?) as total
2015-04-01 17:00:07,858 [main] DEBUG com.mogu.mapper.TopicMapper.pageQuery    -==>  Preparing: select count(*) from (SELECT * FROM mogu_user_topic ORDER BY ? ?) as total
2015-04-01 17:00:07,930 [main] DEBUG com.mogu.mapper.TopicMapper.pageQuery    -==> Parameters: null, null
2015-04-01 17:00:07,935 [main] ERROR com.mogu.util.page.PageInterceptor       -You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'null) as total' at line 5
com.mysql.jdbc.exceptions.jdbc4.M

在第1种count的时候居然取不到所传递的两个值信息,而在第2.种的使用没有问题:

2015-04-01 16:55:43,580 [main] DEBUG org.mybatis.spring.SqlSessionUtils       -Creating a new SqlSession
2015-04-01 16:55:43,588 [main] DEBUG org.mybatis.spring.SqlSessionUtils       -Registering transaction synchronization for SqlSession [[email protected]]
2015-04-01 16:55:43,686 [main] DEBUG ing.transaction.SpringManagedTransaction -JDBC Connection [jdbc:mysql://120.24.245.201:3306/mogubrothers?useUnicode=true&characterEncoding=utf8, [email protected], MySQL-AB JDBC Driver] will be managed by Spring
2015-04-01 16:55:43,693 [main] DEBUG com.mogu.mapper.TopicMapper.pageQuery    -ooo Using Connection [jdbc:mysql://120.24.245.201:3306/mogubrothers?useUnicode=true&characterEncoding=utf8, [email protected], MySQL-AB JDBC Driver]
2015-04-01 16:55:43,699 [main] DEBUG com.mogu.util.page.PageInterceptor       -page query
2015-04-01 16:55:43,699 [main] DEBUG com.mogu.util.page.PageInterceptor       -sqlCount: select count(*) from (SELECT * FROM mogu_user_topic

		            ORDER BY posttime DESC) as total
2015-04-01 16:55:43,701 [main] DEBUG com.mogu.mapper.TopicMapper.pageQuery    -==>  Preparing: select count(*) from (SELECT * FROM mogu_user_topic ORDER BY posttime DESC) as total
2015-04-01 16:55:43,771 [main] DEBUG com.mogu.mapper.TopicMapper.pageQuery    -==> Parameters:
2015-04-01 16:55:43,792 [main] DEBUG com.mogu.util.page.PageInterceptor       -page query count: 19
2015-04-01 16:55:43

所以排序时候使用$处理

时间: 2024-10-29 05:14:22

mybatis中的分页并且带了排序效果的相关文章

orcal分页查询带条件排序

<select id="queryInjuredCase" resultMap="BaseResultMap"> SELECT aa.* FROM (SELECT a.*,rownum rn FROM (SELECT * FROM INJURED_CASE where 1 = 1 AND status = 1 <if test="qicp != null"> <if test="qicp.vinNum !=

在ASP.NET MVC5中实现具有服务器端过滤、排序和分页的GridView

背景 在前一篇文章<[初学者指南]在ASP.NET MVC 5中创建GridView>中,我们学习了如何在 ASP.NET MVC 中实现 GridView,类似于 ASP.NET web 表单的功能.通过前文,我们已经了解到使用 jQuery 插件的数据表可以很容易地实现具有搜索.排序和分页等重要功能的表格. 前文中需要注意的是,所有通过插件实现的特性都是客户端的,这意味着所有的数据都首先在页面载入,然后由插件来处理客户端搜索.分页和排序的数据.如果数据表不是特别大,这么做是可以的:但是,如

数据库中复杂的联查+筛选条件+排序+分页

数据库中复杂的联查+筛选条件+排序+分页一直是比较头疼的问题 为了模拟这个问题,首先建立两个表 create table t_userType ( id int identity(1,1) not null primary key, name varchar(50) ) GO create table t_user ( id int identity(1,1) not null primary key, t_userTypeId int not null, name varchar(50), f

(经典map)A - Hardwood Species(7.1.1)(利用STL中自带的排序功能编程的实验范例)(转)

Description Hardwoods are the botanical group of trees that have broad leaves, produce a fruit or nut, and generally go dormant in the winter. America's temperate climates produce forests with hundreds of hardwood species -- trees that share certain

mybatis中的oracle和mysql分页

这段时间一直在用mybatis+spring+springMVC的框架,总结点东西吧. mybatis的oracle分页写法: <?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&quo

大神带你重新认识Mybatis中强大的resultMap

前言在Mybatis中,有一个强大的功能元素resultMap.当我们希望将JDBC ResultSets中的数据,转化为合理的Java对象时,你就能感受到它的非凡之处.正如其官方所述的那样: resultMap元素是 MyBatis 中最重要最强大的元素.它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作.实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的长达

MyBatis 杂项(分页,缓存,处理BLOB\CLOB数据)

1.处理CLOB,BLOB数据 oracle中的 clob:clob blob:blobmysql中的 clob:longtext blob:longblob 2.传入多个输入参数,mybatis自带的param属性(但是不经常用,我们用map就足够了) 3.MyBatis分页 逻辑分页:将数据全部取出先放到内存中,之后在内存中进行分页,性能不好.不推荐使用 物理分页:通过语句进行分页. 4.MyBatis缓存 MyBatis默认情况下:MyBatis默认使用一级缓存,即同一个SqlSessio

mybatis常用经典分页方法

来自棱镜学院-在线IT教育www.prismcollege.com 分页方法一: 可以查看如下代码,新建一个数据库分页基础类 package com.ssm.utils.pagination.pagebounds; import java.util.List; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spr

Mybatis最入门---分页查询(拦截器分页原理及实现)

[一步是咫尺,一步即天涯] 前文,我们演示了物理分页的Sql实现方式,这种方式使得我们每次在编写查询服务时,不断的重复造轮子.这样的代码实现方式就显得十分的笨拙了.本文是Mybatis分页查询的最后一片内容,我们将介绍基于拦截器的,精巧的实现方式.在阅读这篇文章之前,强烈建议各位看官能够先阅读上文.这样就能对下文我们提及的各种对象及他们之间的关系有一个清晰的关系.好了,废话不多讲,开始我们的正文部分吧. 准备工作: a.操作系统 :win7 x64 b.基本软件:MySQL,Mybatis,SQ