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

1 0
原创粉丝点击