MyBatis
MyBatis内部大概实现原理是用一个List 保存Key的位置,然后用一个Map对应Key的值
复制粘贴网上代码,终于出来了。。。
==========================
一定要紧跟要分页的dao查询语句的上面,如果有多个查询语句以startPage下面第一个才会被分页
public Page getBySoftware(int softwareId,int pageNum,int pageSize,int oprId) {
Page page=PageHelper.startPage(pageNum, pageSize, SoftwareUser.class);
dao.selectBySoftware(softwareId);//这里也会返回分页后的 数据 page中的result 中也有保存
return page;
}
或者
Page page=PageHelper.startPage(1, 10, User.class);
List list = dao.selectList();
page.setResult(list);
return page;
Page page=PageHelper.startPage(1, 10, User.class);
dao.selectList();
return page;
Page page=PageHelper.startPage(1, 10,”user_name”,OrderByType.DESC User.class);
dao.selectList();
return page;
**Mysql 分页实现**
--------------
package isxiatian.mybatis.plugin.parser;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.session.Configuration;
import isxiatian.mybatis.plugin.Page;
/**
* mysql 分页
* @author xia
*
*/
public class MySqlParser implements Parser {
//setParameterKeys 是设置sql 的预编译 参数 key 必须是有序列表
//比如 select * from user where name=? limit ?,? 第一个?=key1 第二个?=key2 ....以此类推
//key1,key2 自己随便取,和sql 语句中的 ? 顺序对应就可以了
//setParameterValues 是设置 sql key值 是Map字典 对应上面设置的key 添加对应的 key值就可以了
//比如:key1=李白 , key2=0 key3=10
@Override
public List<ParameterMapping> setParameterKeys(Configuration configuration,
List<ParameterMapping> parameterMappings, Page page) {
List<ParameterMapping> newParameterMappings = new ArrayList<ParameterMapping>();
newParameterMappings.addAll(parameterMappings);
String orderby=page.getOrderby();
if(orderby != null && !orderby.equals("")){
newParameterMappings.add(new ParameterMapping.Builder(configuration, PAGEPARAMETER_ORDERBY, String.class).build());
newParameterMappings.add(new ParameterMapping.Builder(configuration, PAGEPARAMETER_ORDERBYTYPE, String.class).build());
}
newParameterMappings.add(new ParameterMapping.Builder(configuration,PAGEPARAMETER_FIRST,Integer.class).build());
newParameterMappings.add(new ParameterMapping.Builder(configuration,PAGEPARAMETER_SECOND,Integer.class).build());
return newParameterMappings;
}
@Override
public Map<String, Object> setParameterValues(Map<String, Object> parameterMappings, Page page) {
String orderby=page.getOrderby();
Map<String, Object> newMap = parameterMappings;
if(orderby != null && !orderby.equals("")){
newMap.put(PAGEPARAMETER_ORDERBY, page.getOrderby());
newMap.put(PAGEPARAMETER_ORDERBYTYPE,page.getOrderByType());
}
newMap.put(PAGEPARAMETER_FIRST, page.getStartRow());
newMap.put(PAGEPARAMETER_SECOND,page.getPageSize());
return newMap;
}
@Override
public String getPageSql(String sql,Page page) {
StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
sqlBuilder.append(sql);
String orderby=page.getOrderby();
if(orderby != null && !orderby.equals(""))
sqlBuilder.append(" order by ? ?");
sqlBuilder.append(" limit ?,?");
return sqlBuilder.toString();
}
@Override
public String getCountSql(String sql) {
return "select count(0) from (" + sql + ") as " + SUFFIX_COUNT;
}
}
**
=========================
--------------
**MyBatis 拦截器**
**
package isxiatian.mybatis.plugin;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.ibatis.builder.StaticSqlSource;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
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.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.log4j.Logger;
import isxiatian.mybatis.plugin.parser.MySqlParser;
import isxiatian.mybatis.plugin.parser.Parser;
/**
* Mybatis - 通用分页拦截器
* @author xia
*
*/
@Intercepts(@Signature(type = Executor.class, method = “query”, args = { MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class }))
public class PageHelper implements Interceptor,ParserData {
private static final Logger LOGGER = Logger.getLogger(PageHelper.class);
@SuppressWarnings("rawtypes")
private static final ThreadLocal<Page> localPage = new ThreadLocal<Page>();
private static Parser parser = null;
/**
* 开始分页
*
* @param pageNum
* @param pageSize
*/
public static <E> Page<E> startPage(int pageNum, int pageSize,Class<E> resultType) {
Page<E> page=new Page<E>(pageNum, pageSize);
localPage.set(page);
return page;
}
public static <E> Page<E> startPage(int pageNum, int pageSize,String orderby,Class<E> resultType) {
Page<E> page=new Page<E>(pageNum, pageSize,orderby);
localPage.set(page);
return page;
}
public static <E> Page<E> startPage(int pageNum, int pageSize,String orderby,OrderByType orderByType,Class<E> resultType) {
Page<E> page =new Page<E>(pageNum, pageSize,orderby,orderByType);
localPage.set(page);
return page;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Object intercept(Invocation invocation) throws Throwable {
//获取Page
Page page=localPage.get();
if ( page == null || page.getPageSize() <= 0) {
return invocation.proceed();
}
//忽略Mybatis自带的内存分页
RowBounds rowBounds=(RowBounds)invocation.getArgs()[2];
invocation.getArgs()[2] = RowBounds.DEFAULT;
MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];
Object parameter = invocation.getArgs()[1];
//不是查询跳过
if(mappedStatement.getSqlCommandType() != SqlCommandType.SELECT)
return invocation.proceed();
Configuration configuration = mappedStatement.getConfiguration();
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
//获取连接
Connection connection=mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection();
//重设分页参数里的总页数等
setPageParameter(boundSql.getSql(), connection, mappedStatement, boundSql, page);
//越界则取最后一页
if(page.getEndRow()>page.getPages()*page.getPageSize())
{
page.setPageNum(page.getPages());
page.setStartRow((page.getPageNum()-1)*page.getPageSize());
page.setEndRow(page.getPageNum()*page.getPageSize());
}
//获取参数 列表
List<ParameterMapping> newParameterMappings =parser.setParameterKeys(configuration, boundSql.getParameterMappings(), page);
//获取 分页sql
String newSql=parser.getPageSql(boundSql.getSql(),page);
StaticSqlSource sqlsource = new StaticSqlSource(configuration,newSql,newParameterMappings);
//替换ms
invocation.getArgs()[0]=newMappedStatement(mappedStatement,sqlsource,SUFFIX_PAGE);
//获取参数 数值
Map<String, Object> map = setPageParameter(mappedStatement,parameter,mappedStatement.getBoundSql(parameter));
Map<String, Object> newMap=parser.setParameterValues(map, page);
int i=1;
//防止使用 sql 使用 下标作为参数 报错
for (ParameterMapping parameterMapping : newParameterMappings) {
newMap.put("param"+i++, newMap.get(parameterMapping.getProperty()));
}
//替换参数
invocation.getArgs()[1]=newMap;
Object result = invocation.proceed();
page.setResult((List)result);
localPage.remove();
//还原
invocation.getArgs()[0]=mappedStatement;
invocation.getArgs()[1]=parameter;
invocation.getArgs()[2]=rowBounds;
return result;
}
/**
* 获取总记录数
*
* @param sql
* @param connection
* @param mappedStatement
* @param boundSql
* @param page
*/
@SuppressWarnings("rawtypes")
private void setPageParameter(String sql, Connection connection, MappedStatement mappedStatement, BoundSql boundSql,
Page page) {
// 记录总记录数
String countSql = parser.getCountSql(sql);
PreparedStatement countStmt = null;
ResultSet rs = null;
try {
countStmt = connection.prepareStatement(countSql);
BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,
boundSql.getParameterMappings(), boundSql.getParameterObject());
//设置原来的动态参数 比如循环之类的参数
for(ParameterMapping parameterMapping:boundSql.getParameterMappings()){
if(boundSql.hasAdditionalParameter(parameterMapping.getProperty()))
countBS.setAdditionalParameter(parameterMapping.getProperty(), boundSql.getAdditionalParameter(parameterMapping.getProperty()));
}
setParameters(countStmt, mappedStatement, countBS, boundSql.getParameterObject());
rs = countStmt.executeQuery();
int totalCount = 0;
if (rs.next()) {
totalCount = rs.getInt(1);
}
page.setTotal(totalCount);
int totalPage = totalCount / page.getPageSize() + ((totalCount % page.getPageSize() == 0) ? 0 : 1);
page.setPages(totalPage);
} catch (SQLException e) {
LOGGER.error("Ignore this exception", e);
} finally {
try {
rs.close();
} catch (SQLException e) {
LOGGER.error("Ignore this exception", e);
}
try {
countStmt.close();
} catch (SQLException e) {
LOGGER.error("Ignore this exception", e);
}
}
}
/**
* 代入参数值
*
* @param ps
* @param mappedStatement
* @param boundSql
* @param parameterObject
* @throws SQLException
*/
private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,
Object parameterObject) throws SQLException {
ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler.setParameters(ps);
}
/**
* 创建 MappedStatement
* @param ms
* @param sqlSource
* @param suffix MappedStatement id
* @return MappedStatement
*/
private MappedStatement newMappedStatement(MappedStatement ms, SqlSource sqlSource,String suffix) {
String id = ms.getId() + suffix;
MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), id, sqlSource, ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {
StringBuilder keyProperties = new StringBuilder();
for (String keyProperty : ms.getKeyProperties()) {
keyProperties.append(keyProperty).append(",");
}
keyProperties.delete(keyProperties.length() - 1, keyProperties.length());
builder.keyProperty(keyProperties.toString());
}
builder.timeout(ms.getTimeout());
builder.parameterMap(ms.getParameterMap());
builder.resultMaps(ms.getResultMaps());
builder.resultSetType(ms.getResultSetType());
builder.cache(ms.getCache());
builder.flushCacheRequired(ms.isFlushCacheRequired());
builder.useCache(ms.isUseCache());
return builder.build();
}
/**
* 获取参数
* @param ms
* @param parameterObject
* @param boundSql
* @return
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public Map<String, Object> setPageParameter(MappedStatement ms, Object parameterObject, BoundSql boundSql) {
Map<String, Object> paramMap = null;
if (parameterObject == null) {
paramMap = new HashMap<String, Object>();
} else if (parameterObject instanceof Map) {
//解决不可变Map的情况
paramMap = new HashMap<String, Object>();
paramMap.putAll((Map) parameterObject);
} else {
paramMap = new HashMap<String, Object>();
//动态sql时的判断条件不会出现在ParameterMapping中,但是必须有,所以这里需要收集所有的getter属性
//TypeHandlerRegistry可以直接处理的会作为一个直接使用的对象进行处理
boolean hasTypeHandler = ms.getConfiguration().getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass());
if (!hasTypeHandler) {
MetaObject metaObject = SystemMetaObject.forObject(parameterObject);
for (String name : metaObject.getGetterNames()) {
paramMap.put(name, metaObject.getValue(name));
}
}
//下面这段方法,主要解决一个常见类型的参数时的问题
if (boundSql.getParameterMappings() != null && boundSql.getParameterMappings().size() > 0) {
for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
String name = parameterMapping.getProperty();
if (!name.equals(PAGEPARAMETER_FIRST)
&& !name.equals(PAGEPARAMETER_SECOND)
&& paramMap.get(name) == null) {
if (hasTypeHandler
|| parameterMapping.getJavaType().equals(parameterObject.getClass())) {
paramMap.put(name, parameterObject);
break;
}
}
}
}
}
//设置原来的动态参数 比如循环之类的参数
for(ParameterMapping parameterMapping:boundSql.getParameterMappings()){
if(boundSql.hasAdditionalParameter(parameterMapping.getProperty()))
paramMap.put(parameterMapping.getProperty(), boundSql.getAdditionalParameter(parameterMapping.getProperty()));
}
return paramMap;
}
@Override
public Object plugin(Object target) {
if (target instanceof Executor)
return Plugin.wrap(target, this);
return target;
}
@Override
public void setProperties(Properties properties) {
//获取 分页插件实现 默认Mysql
String sqlparser=properties.getProperty("sqlParser");
if(sqlparser != null && !"".equals(sqlparser))
{
Exception exception=null;
//创建分页插件对象
try {
Class<?> parserClass = Class.forName(sqlparser);
if(Parser.class.isAssignableFrom(parserClass))
parser=(Parser)parserClass.newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
exception=e;
}
if(parser == null && exception != null)
throw new RuntimeException(exception);
}
else
{
parser=new MySqlParser();
}
}
}
使用方式
----
**1.让拦截器生效**
我这里没有 吧MyBatis 配置文件单独分出来,而是和spring 配置文件结合了
sqlParser=isxiatian.mybatis.plugin.parser.MySqlParser
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:dao/mapper/*.xml" />
</bean>
**如果是单独的 MyBatis 配置文件 参考看下面,这一种我没用过,不行的话,自行脑补**
<plugins>
<plugin interceptor="isxiatian.mybatis.plugin.PageHelper">
<property name="sqlParser" value="isxiatian.mybatis.plugin.parser.MySqlParser" />
</plugin>
</plugins>
**2.代码上的使用**
注意 PageHelper.startPage 下面 的第一个查询 会进行分页
后面分页 需要继续 调用 PageHelper.startPage
PageHelper.startPage 下面第一个一定要进行分页的查询,不要把不需要分页的查询放在 PageHelper.startPage 下面的第一个
public Page getBySoftware(int softwareId,int pageNum,int pageSize,int oprId) {
Page page=PageHelper.startPage(pageNum, pageSize, SoftwareUser.class);
dao.selectBySoftware(softwareId);//这里也会返回分页后的 数据 page中的result 中也有保存
return page;
}
或者
Page page=PageHelper.startPage(1, 10, User.class);
List list = dao.selectList();
page.setResult(list);
return page;
Page page=PageHelper.startPage(1, 10, User.class);
dao.selectList();
return page;
Page page=PageHelper.startPage(1, 10,”user_name”,OrderByType.DESC User.class);
dao.selectList();
return page;
利用Jackson 返回JSON数据
Page page=PageHelper.startPage(1, 10, User.class);
dao.selectList();
return Json.getJson(page);
以上都是正确的使用方式
**下面这个是错误的**
Page page=PageHelper.startPage(1, 10, User.class);
User user=dao.selectId(1);//这里这样写的话,会把 selectId 变成分页语句,后面真正需要分页的没有进行分页
dao.selectList();
return page;
“`
代码