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

来源:互联网 发布:苹果数据线头焊接 编辑:程序博客网 时间:2024/06/05 08:17
第一个做法,就是直接使用我们的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!\n" + 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

0 0