MyBatis 最简单的分页,贼几把方便,兄弟,不吹逼
来源:互联网 发布:VPN网络 编辑:程序博客网 时间:2024/04/30 04:40
public Page<SoftwareUser> getBySoftware(int softwareId,int pageNum,int pageSize,int oprId) { Page<SoftwareUser> page=PageHelper.startPage(pageNum, pageSize, SoftwareUser.class); dao.selectBySoftware(softwareId);//这里也会返回分页后的 数据 page中的result 中也有保存 return page; }
或者
Page<User> page=PageHelper.startPage(1, 10, User.class);List<User> list = dao.selectList();page.setResult(list);return page;
Page<User> page=PageHelper.startPage(1, 10, User.class);dao.selectList();return page;
Page<User> page=PageHelper.startPage(1, 10,"user_name",OrderByType.DESC User.class);dao.selectList();return page;
利用Jackson 返回JSON数据
Page<User> page=PageHelper.startPage(1, 10, User.class);dao.selectList();return Json.getJson(page);
看了很多别人写的分页插件,都是上古年间的,最新的 MyBatis 3.4 不支持了。
然后 得知 PageHelper 这个分页插件很牛逼,然后看了他的配置还是挺多的,麻烦,有一些我又不懂。。。想看他代码自己写个。。。。。
后来参考了
好几个不同的+上古年间的分页插件+自己分析MyBatis
终于写出来了,我想要的插件,因为上古年间的插件是直接拼接 Sql语句,如果我需要分页排序,由页面前端传递排序 列名 ,
就存在sql 注入,当然可以用 代号 来表示列名,防止直接由页面前端传入 需要排序的列名 比如 (1=userName,2=registerTime,3=level。。。。)
把老子的意大利炮拿来
下面是实现分页接口
package isxiatian.mybatis.plugin.parser;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;import isxiatian.mybatis.plugin.ParserData;/** * 分页接口 * @author xia * */public interface Parser extends ParserData{ //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 //getPageSql 获取分页 sql ,sql参数 请尽量使用 ? 防止sql注入 //getCountSql 获取总数sql /** * 设置 sql 参数 * @param configuration * @param parameterMappings 原参数 * @param page * @return 有序参数 List集合 */ @SuppressWarnings("rawtypes") List<ParameterMapping> setParameterKeys(Configuration configuration,List<ParameterMapping> parameterMappings,Page page); /** * 设置sql 参数对应数值 * @param parameterMappings 原数值 * @param page * @return Map对象 */ @SuppressWarnings("rawtypes") Map<String, Object> setParameterValues(Map<String, Object> parameterMappings,Page page); /** * 修改原SQL为分页SQL * @param sql * @return 新的sql */ @SuppressWarnings("rawtypes") String getPageSql(String sql,Page page); /** * 获取数量sql * @param sql * @return 新的sql */ String getCountSql(String sql);}
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()); 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; } } } } } 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 配置文件结合了
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 数据源 --> <property name="dataSource" ref="dataSource" /> <!-- 拦截器 --> <property name="plugins"> <array> <bean class="isxiatian.mybatis.plugin.PageHelper"> <property name="properties"> <value>sqlParser=isxiatian.mybatis.plugin.parser.MySqlParser</value> </property> </bean> </array> </property> <!-- 延迟加载 配置 具体自己查询资料 --> <property name="configuration"> <bean class="org.apache.ibatis.session.Configuration"> <property name="lazyLoadingEnabled" value="true"/> <property name="aggressiveLazyLoading" value="true"/> </bean> </property> <!-- 自动扫描mapping.xml文件 --> <property name="mapperLocations" value="classpath:dao/mapper/*.xml" /> </bean>
如果是单独的 MyBatis 配置文件 参考看下面,这一种我没用过,不行的话,自行脑补
<configuration> <!-- 基础设置 --> <settings> <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。--> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="true"/> <setting name="cacheEnabled" value="true"/> </settings> <plugins> <plugin interceptor="isxiatian.mybatis.plugin.PageHelper"> <property name="sqlParser" value="isxiatian.mybatis.plugin.parser.MySqlParser" /> </plugin> </plugins></configuration>
2.代码上的使用
注意 PageHelper.startPage 下面 的第一个查询 会进行分页
后面分页 需要继续 调用 PageHelper.startPage
PageHelper.startPage 下面第一个一定要进行分页的查询,不要把不需要分页的查询放在 PageHelper.startPage 下面的第一个
public Page<SoftwareUser> getBySoftware(int softwareId,int pageNum,int pageSize,int oprId) { Page<SoftwareUser> page=PageHelper.startPage(pageNum, pageSize, SoftwareUser.class); dao.selectBySoftware(softwareId);//这里也会返回分页后的 数据 page中的result 中也有保存 return page; }
或者
Page<User> page=PageHelper.startPage(1, 10, User.class);List<User> list = dao.selectList();page.setResult(list);return page;
Page<User> page=PageHelper.startPage(1, 10, User.class);dao.selectList();return page;
Page<User> page=PageHelper.startPage(1, 10,"user_name",OrderByType.DESC User.class);dao.selectList();return page;
利用Jackson 返回JSON数据
Page<User> page=PageHelper.startPage(1, 10, User.class);dao.selectList();return Json.getJson(page);
以上都是正确的使用方式
下面这个是错误的
Page<User> page=PageHelper.startPage(1, 10, User.class);User user=dao.selectId(1);//这里这样写的话,会把 selectId 变成分页语句,后面真正需要分页的没有进行分页dao.selectList();return page;
顺便提一下,Mybatis 返回的对象 是代理类,他会在里面加一些东西,在进行序列化JSON数据的时候 会导致嵌套异常
所以需要把他排除掉
在返回 的数据 类型 class 头上 加上注解就可以了
例如:
@SuppressWarnings("serial")@JsonIgnoreProperties("handler")public class User implements Serializable{}
已经有 Mysql,oracle,sqlserver2012 数据库的分页实现
最后把代码发一下,里面我已经尽可能做了很多注释了,如果你觉得这个写的不好,对于你这种人,我只想说,求一份源码
这个插件并不适合所有场景,也算是抛砖引玉吧,希望大家,看完后能写出适合自己的插件
比如:如果进行频繁查询,还是同一个查询语句的话,那么 MappedStatement 也会被频繁创建,这里可以把 MappedStatement 保存起来,后面在查询的话直接获取,就不用在创建了,效率会提高不少的,PageHelper 就是这么做的,我这个其中也参考了 PageHelper 等其他插件,说真的他那个里面注释很少。
……嘛..如果你觉得我说的可以,请记得 充电订阅投硬币丢香蕉给我
上传到 csdn 上要等一下,然后想传到百度云上,但是百度云好像挂了,说 服务器错误……..我里面还有很多电影呢
链接
http://download.csdn.net/detail/carlyle123/9649842
- MyBatis 最简单的分页,贼几把方便,兄弟,不吹逼
- Gridview最方便的自定义分页
- mybatis-page最简单分页插件
- 最简单的mybatis
- MyBatis+Mysql 实现分页(最简单通用的分页方式)
- 最方便简单的经纬度查询方法
- 在SpringMVC+Mybatis中一个很方便的分页方法
- MyBatis分页的简单实现
- asp最简单的分页
- 最最最简单的分页
- 最简单的分页方法
- 最简单的分页算法
- GridView最简单的分页
- 最简单的假分页
- oracle最简单的分页
- 最简单的php分页
- 简单,方便,功能全的php分页类
- 简单,方便,功能全的php分页类
- std::cerr与std::cout区别
- LINUX下python程序的运行
- 车道检测
- UVa 232 纵横字谜的答案
- Volatile
- MyBatis 最简单的分页,贼几把方便,兄弟,不吹逼
- 不知道大小的图片在盒子中垂直居中
- poj 1256
- Fragment基本使用(三)——与Activity之间传递数据
- 车道检测(二)
- 有关于CSS的面试题和练习
- Js文档翻译---Promise
- 机器学习1
- break语句