MyBatis分页

MyBatis分页

最早使用普通JDBC使用分页的时候,都是

SELECT * FROM students LIMIT num1,num2;

意思就是从students这个表中找第num1的记录开始往下的num2个记录。

每次分页都得计算总条数,然后计算页数等等。后来使用MyBatis后, 该如何进行分页呢?当然也可以用以上的方法进行传参,但是每次遇到分页都得重写一个,很麻烦。

实现MyBatis的拦截器实现

MyBatis拦截器是基于Java动态代理实现的。动态代理知识可参考http://blog.csdn.net/qq407388356/article/details/79313972。

页面实体类Page.java

首先写一个Page的pojo类来封装分页的一些数据信息:

public class Page {
    private int totalNumber;//总条数
    private int currentPage;//当前第几页
    private int totalPage;//总页数
    private int pageNumber = 5;//每页显示条数
    private int dbIndex;//数据库中limit的参数,从第几条开始取
    private int dbNumber;//数据库中limit的参数,一共取多少条

    /**
     * 根据当前对象中属性值计算并设置相关属性值
     */
    public void count() {
        // 计算总页数
        int totalPageTemp = this.totalNumber / this.pageNumber;
        int plus = (this.totalNumber % this.pageNumber) == 0 ? 0 : 1;
        totalPageTemp = totalPageTemp + plus;
        if (totalPageTemp <= 0) {
            totalPageTemp = 1;
        }
        this.totalPage = totalPageTemp;

        // 设置当前页数
        // 总页数小于当前页数,应将当前页数设置为总页数
        if (this.totalPage < this.currentPage) {
            this.currentPage = this.totalPage;
        }
        // 当前页数小于1设置为1
        if (this.currentPage < 1) {
            this.currentPage = 1;
        }

        // 设置limit的参数
        this.dbIndex = (this.currentPage - 1) * this.pageNumber;
        this.dbNumber = this.pageNumber;
    }

    //设置totalNumber时候需要计算count
    public void setTotalNumber(int totalNumber) {
        this.totalNumber = totalNumber;
        this.count();
    }

    //设置pageNumber时候需要计算count
    public void setPageNumber(int pageNumber) {
        this.pageNumber = pageNumber;
        this.count();
    }
...//其他settergetter}

mapper文件

在StudentsMapper.java接口中声明该方法:

List<Students> selectAllByPage(Map<String,Object>parameter);

在StudentsMapper.xml文件中配置对应id的sql语句,注意parameterType需要传入page对象的信息在该map中,其他查询信息也可放入该map中。

<select id="selectAllByPage"parameterType="java.util.Map" resultMap="BaseResultMap">
 
select id, name, age, hobby,classAndGradeId
  from students
</select>

分页拦截器PageInterceptor.java

编写分页拦截器,实现org.apache.ibatis.plugin.Interceptor接口。

@Signature(type=StatementHandler.class,method="prepare",args={Connection.class}表示我们要拦截StatementHandler接口下面的prepare的方法,参数是Connection.class。因为该方法正是处理我们提交sql的方法,因此拦截后可以做修改(加入分页功能)。

根据拦截下来的SQL的id是否ByPage结尾(使用正则表达式匹配),这个可以在一个项目中统一规范哪些SQL需要分页。如果是,则进行SQL语句修改,通过参数中Page对象的信息进行分页查询。

具体代码如下:

/**
 * 分页拦截器
 */
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
public class PageInterceptor implements Interceptor {

   @Override
   public Object intercept(Invocation invocation) throws Throwable {
      StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
      MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
      MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
      // 配置文件中SQL语句的ID
      String id = mappedStatement.getId();
      if(id.matches(".+ByPage$")) {
         BoundSql boundSql = statementHandler.getBoundSql();
         // 原始的SQL语句
         String sql = boundSql.getSql();
         // 查询总条数的SQL语句
         String countSql = "select count(*) from (" + sql + ")a";
         Connection connection = (Connection)invocation.getArgs()[0];
         PreparedStatement countStatement = connection.prepareStatement(countSql);
         ParameterHandler parameterHandler = (ParameterHandler)metaObject.getValue("delegate.parameterHandler");
         parameterHandler.setParameters(countStatement);
         ResultSet rs = countStatement.executeQuery();
         
         Map<?,?> parameter = (Map<?,?>)boundSql.getParameterObject();
         Page page = (Page)parameter.get("page");
         if(rs.next()) {
            page.setTotalNumber(rs.getInt(1));
         }
         // 改造后带分页查询的SQL语句
         String pageSql = sql + " limit " + page.getDbIndex() + "," + page.getDbNumber();
         metaObject.setValue("delegate.boundSql.sql", pageSql);
      }
      return invocation.proceed();
   }

   @Override
   public Object plugin(Object target) {
      return Plugin.wrap(target, this);
   }

   @Override
   public void setProperties(Properties properties) {
   }

}

添加插件mybatis-config.xml

在mybatis的配置文件中添加插件配置,也就是我们刚刚编写的那个实现Interceptor接口的类。

<plugins>
    <plugin interceptor="com.seu.fn.interceptor.PageInterceptor">
    </plugin>
</plugins>

测试

参数中需要在map中传入“page”对象,如果需要同时也可以map.put()其他的查询条件。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring/applicationContext-dao.xml"})
public class StudentsMapperTest {
    @Autowired
    StudentsMapper studentsMapper;

    @Test
    public void selectAllByPage(){
        Map<String,Object> map = new HashMap<>();
        Page page = new Page();
        page.setCurrentPage(2);
        map.put("page",page);
        List<Students> list = studentsMapper.selectAllByPage(map);
        for (Students s:list){
            System.out.println(s);
        }
    }
}

测试输出:

Students{id='06', name='name6', age=null,hobby='null', classandgradeid='1'}

Students{id='07', name='name7', age=null,hobby='null', classandgradeid='1'}

Students{id='08', name='name8', age=null,hobby='null', classandgradeid='1'}

Students{id='09', name='name9', age=null,hobby='null', classandgradeid='1'}

Students{id='10', name='name10', age=null,hobby='null', classandgradeid='1'}

使用PageHelper插件实现

PageHelper插件用起来比较方便,不用改自己的代码。该方法其实也是基于拦截器实现的,只是用起来更方便,不用自己实现Interceptor接口,只需要在返回List<?>的select方法前调用PageHelper.startPage(int,int)方法,即可。

pom.xml依赖:

<dependency>
  <groupId>
com.github.pagehelper</groupId>
  <artifactId>
pagehelper</artifactId>
  <version>
5.1.2</version>
</dependency>

在mybatis-config.xml中配置拦截器插件(同上):

<plugins>
    <!-- com.github.pagehelperPageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- 设置数据库类型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六种数据库可以自动识别,不用再配置下面的property-->
        <!--<property name="dialect" value="mysql"/>-->
    </plugin>
</plugins>

mapper文件:

在StudentsMapper.java接口中声明该方法(不需要传page信息参数)。

List<Students> selectAll();

在StudentsMapper.xml文件中配置对应id的sql语句,普通的SQL语句配置。

<select id="selectAll"resultMap="BaseResultMap">
 
select id, name, age, hobby,classAndGradeId
  from students
</select>

如何使用:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring/applicationContext-dao.xml"})
public class StudentsMapperTest {
    @Autowired
    StudentsMapper studentsMapper;
    @Test
    public void selectAll() {
        //获取第2页,5条内容,默认查询总数count
        PageHelper.startPage(2, 5);
        //紧跟着的第一个select方法会被分页
        List<Students> list = studentsMapper.selectAll();
        PageInfo page = new PageInfo(list);
        //PageInfo包含了非常全面的分页属性
        System.out.println(page.getPageNum());//2
        System.out.println(page.getPageSize());//5
        System.out.println(page.getStartRow());//6
        System.out.println(page.getEndRow());//10
        System.out.println(page.getTotal());//15
        System.out.println(page.getPages());//3
        System.out.println(page.isIsFirstPage());//false
        System.out.println(page.isIsLastPage());//false
        System.out.println(page.isHasPreviousPage());//true
        System.out.println(page.isHasNextPage());//true
        for (Students s:list){
            System.out.println(s);
        }
    }
}

测试输出:

Students{id='06', name='name6', age=null,hobby='null', classandgradeid='1'}

Students{id='07', name='name7', age=null,hobby='null', classandgradeid='1'}

Students{id='08', name='name8', age=null,hobby='null', classandgradeid='1'}

Students{id='09', name='name9', age=null,hobby='null', classandgradeid='1'}

Students{id='10', name='name10', age=null,hobby='null', classandgradeid='1'}

和上面结果相同,这种方法是使用第三方插件(实现原理都相同),比较方便,容易上手。

猜你喜欢

转载自blog.csdn.net/qq407388356/article/details/79516292