• 如何做系列(1)- mybatis 如何实现分页?


    第一个做法,就是直接使用我们的sql语句进行分页,也就是在mapper里面加上分页的语句就好了。

    <select id="" parameterType="" resultType="" resultMap=""> 
    Select ROWNUM,ID,NAME FROM(Select ROWNUM as ROWNO, ID,NAME from CHANGED_CONTENT
    <where> 
    <![CDATA[ROWNUM <= #{endRow}]]> 
    </where>
    ) 
    <where>
    <![CDATA[ROWNO > #{startRow}]]> 
    </where>
    </select>

    然后我们在使用这个dao的时候,传入我们的分页的参数,就可以实现我们的分页需求了。其实很简单。

    第二个做法,是一个更为通用的做法,那就是利用mybatis的拦截器,拦截每一个sql,在需要分页的sql加上分页的语句,就可以实现我们的分页功能。

    我们可以查看 org.mybatis.spring.SqlSessionFactoryBean,其实他有一个plugin属性,我们可以配置实现一个插件来实现我们的需求。

    首先说明一下实现的流程

    例如我们有一条sql

    String sql =select * from tableXXX where a=xxxx

    我们的 拦截器拦截这条sql,然后判断是否是需要分页的,然后将这条sql转化为分页的sql

    <span style="font-family: 微软雅黑; background-color: rgb(255, 255, 255);">    String countSql = "select count(1) from (" + sql    + ") tmp_count"  </span>
    这样我们就可以得到分页的总页数了。

    然后, 拼上我们传入的分页数据,哪一页,每一页几条数据:

    select * from ( select row_.*, rownum rownum_ from (   sql        ) row_ ) where rownum_ <=  endString   and rownum_ >  offsetPlaceholder

    这样我们就可以得到我们本页的返回了。

    具体的代码如下:

    首先是实现了ibatis intercepter的pageplugin
    public class PagePlugin implements Interceptor {
    
        private static Logger log = Logger.getLogger("page plugin");
        private static Dialect dialectObject = null; // 数据库方言
        private static String pageSqlId = ""; // mybaits的数据库xml映射文件中需要拦截的ID(正则匹配)
    
        public Object intercept(Invocation ivk) throws Throwable {
            if (ivk.getTarget() instanceof RoutingStatementHandler) {
                log.info("com.dragon.dao.pulgin.mybatis.plugin.PagePlugin.intercept() enter*****************");
                RoutingStatementHandler statementHandler = (RoutingStatementHandler) ivk
                        .getTarget();
                BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper
                        .getValueByFieldName(statementHandler, "delegate");
                MappedStatement mappedStatement = (MappedStatement) ReflectHelper
                        .getValueByFieldName(delegate, "mappedStatement");
                /**
                 * 方法1:通过ID来区分是否需要分页..*query.* 方法2:传入的参数是否有page参数,如果有,则分页,
                 */
                if (mappedStatement.getId().matches(pageSqlId)) { // 拦截需要分页的SQL
                    BoundSql boundSql = delegate.getBoundSql();
                    Object parameterObject = boundSql.getParameterObject();// 分页SQL<select>中parameterType属性对应的实体参数,即Mapper接口中执行分页方法的参数,该参数不得为空
                    if (parameterObject == null) {
                        //throw new NullPointerException("boundSql.getParameterObject() is null!");
                        return ivk.proceed();
                    } else {
    
                        PageView pageView = null;
                        if (parameterObject instanceof PageView) { // 参数就是Pages实体
                            pageView = (PageView) parameterObject;
                        } else if (parameterObject instanceof Map) {
                            for (Entry entry : (Set<Entry>) ((Map) parameterObject).entrySet()) {
                                if (entry.getValue() instanceof PageView) {
                                    pageView = (PageView) entry.getValue();
                                    break;
                                }
                            }
                        } else { // 参数为某个实体,该实体拥有Pages属性
                            pageView = ReflectHelper.getValueByFieldType(
                                    parameterObject, PageView.class);
                            if (pageView == null) {
                                return ivk.proceed();
                            }
                        }
    
                        String sql = boundSql.getSql();
                        PreparedStatement countStmt = null;
                        ResultSet rs = null;
                        try {
                            Connection connection = (Connection) ivk.getArgs()[0];
                            String countSql = "select count(1) from (" + sql
                                    + ") tmp_count"; // 记录统计
                            countStmt = connection.prepareStatement(countSql);
                            ReflectHelper.setValueByFieldName(boundSql, "sql",
                                    countSql);
                            DefaultParameterHandler parameterHandler = new DefaultParameterHandler(
                                    mappedStatement, parameterObject, boundSql);
                            parameterHandler.setParameters(countStmt);
                            rs = countStmt.executeQuery();
                            Long count = 0L;
                            if (rs.next()) {
                                count = ((Number) rs.getObject(1)).longValue();
                            }
                            pageView.setRowCount(count);
                        } finally {
                            try {
                                rs.close();
                            } catch (Exception e) {
                            }
                            try {
                                countStmt.close();
                            } catch (Exception e) {
                            }
                        }
                        String pageSql = generatePagesSql(sql, pageView);
                        ReflectHelper.setValueByFieldName(boundSql, "sql", pageSql); // 将分页sql语句反射回BoundSql.
                    }
                }
            }
            return ivk.proceed();
        }
    
        /**
         * 根据数据库方言,生成特定的分页sql
         *
         * @param sql
         * @param page
         * @return
         */
        private String generatePagesSql(String sql, PageView page) {
            if (page != null && dialectObject != null) {
                //pageNow默认是从1,而已数据库是从0开始计算的.所以(page.getPageNow()-1)
                int pageNow = page.getPageNow();
                return dialectObject.getLimitString(sql, (pageNow <= 0 ? 0 : pageNow - 1)
                        * page.getPageSize(), page.getPageSize());
            }
            return sql;
        }
    
        public Object plugin(Object target) {
            return Plugin.wrap(target, this);
        }
    
        public void setProperties(Properties p) {
            String dialect = ""; // 数据库方言
            dialect = p.getProperty("dialect");
            if (StringUtils.isBlank(dialect)) {
                try {
                    throw new PropertyException("dialect property is not found!");
                } catch (PropertyException e) {
                    log.error(e);
                }
            } else {
                try {
                    dialectObject = (Dialect) Class.forName(dialect)
                            .getDeclaredConstructor().newInstance();
                } catch (Exception e) {
                    throw new RuntimeException(dialect + ", init fail!
    " + e);
                }
            }
            pageSqlId = p.getProperty("pageSqlId");//根据id来区分是否需要分页
            if (StringUtils.isBlank(pageSqlId)) {
                try {
                    throw new PropertyException("pageSqlId property is not found!");
                } catch (PropertyException e) {
                    log.error(e);
                }
            }
        }
    }
    


    然后是实现了dialect的oracledialect 数据库方言让我们区分不同的数据库不同的sql语句

    public class OracleDialect extends Dialect {
    
        public boolean supportsLimit() {
            return true;
        }
    
        public boolean supportsLimitOffset() {
            return true;
        }
    
        public String getLimitString(String sql, int offset, String offsetPlaceholder, int limit, String limitPlaceholder) {
            sql = sql.trim();
            boolean isForUpdate = false;
            if (sql.toLowerCase().endsWith(" for update")) {
                sql = sql.substring(0, sql.length() - 11);
                isForUpdate = true;
            }
    
            StringBuffer pagingSelect = new StringBuffer(sql.length() + 100);
            if (offset > 0) {
                pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
            } else {
                pagingSelect.append("select * from ( ");
            }
            pagingSelect.append(sql);
            if (offset > 0) {
    //			int end = offset+limit;
                String endString = offsetPlaceholder + "+" + limitPlaceholder;
                pagingSelect.append(" ) row_ ) where rownum_ <= " + endString + " and rownum_ > " + offsetPlaceholder);
            } else {
                pagingSelect.append(" ) where rownum <= " + limitPlaceholder);
            }
    
            if (isForUpdate) {
                pagingSelect.append(" for update");
            }
    
            return pagingSelect.toString();
        }
    
    }
    


    配置mybatis.xml

    我们需要在sqlSessionFactory中注册我们的插件,发挥效果
     <bean id="pagePlugin" class="com.xxxxx.pulgin.mybatis.plugin.PagePlugin">
            <property name="properties">
                <props>
                    <prop key="dialect">com.dragon.dao.pulgin.jdbc.dialet.OracleDialect</prop>
                    <prop key="pageSqlId">.*query.*</prop>
                </props>
            </property>
        </bean>

     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name="configLocation" value="classpath:spring/mybatis.xml" />
            <property name="plugins">
                <array>
                    <ref bean="pagePlugin" /> 
                   
                </array>
            </property>
            <property name="mapperLocations">
                <list>
                    <!-- 自动匹配Mapper映射文件  -->
                    <value>classpath:com/xxxxx/mapper/*-mapper.xml</value>
                </list>
            </property>        
        </bean>


    至此,我们的分页效果就做出来了。

    charles at P.P 2016-7-14

  • 相关阅读:
    Lua中的loadfile、dofile、require详解
    100GB以上超大文件上传和断点续传服务器的实现
    50GB以上超大文件上传和断点续传服务器的实现
    20GB以上超大文件上传和断点续传服务器的实现
    10GB以上超大文件上传和断点续传服务器的实现
    Flash 以上超大文件上传和断点续传服务器的实现
    WebUploader 以上超大文件上传和断点续传服务器的实现
    HTML5 以上超大文件上传和断点续传服务器的实现
    VUE 以上超大文件上传和断点续传服务器的实现
    B/S 以上超大文件上传和断点续传服务器的实现
  • 原文地址:https://www.cnblogs.com/mrcharles/p/11879825.html
Copyright © 2020-2023  润新知