Mybatis分页解决方案

转自:  http://blog.csdn.net/fover717/article/details/8334209

http://www.cnblogs.com/zemliu/archive/2013/08/07/3242966.html

http://fhd001.iteye.com/blog/1121189

参考:http://blog.csdn.net/isea533/article/details/23831273

http://blog.csdn.net/hupanfeng/article/details/9238127

一,在Spring3中使用MyBatis

1.MyBatis 例子

首先,单独使用MyBatis时:

[java] view plaincopy

  1. import java.io.IOException;
  2. import java.io.Reader;
  3. import org.apache.ibatis.io.Resources;
  4. import org.apache.ibatis.session.SqlSessionFactory;
  5. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  6. public class MyBatisUtil {
  7. // 每一个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心
  8. // 使用SqlSessionFactory的最佳实践是在应用运行期间不要重复创建多次,最佳范围是应用范围
  9. private final static SqlSessionFactory sqlSessionFactory;
  10. static {
  11. String resource = "configuration.xml";
  12. Reader reader = null;
  13. try {
  14. reader = Resources.getResourceAsReader(resource);
  15. } catch (IOException e) {
  16. System.out.println(e.getMessage());
  17. }
  18. // SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象来获得
  19. // SqlSessionFactoryBuilder实例的最佳范围是方法范围(也就是本地方法变量)。
  20. sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  21. }
  22. public static SqlSessionFactory getSqlSessionFactory() {
  23. return sqlSessionFactory;
  24. }
  25. }

配置文件:

[html] view plaincopy

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="Mapper.UserMapper">
  6. <!-- 这里namespace必须是UserMapper接口的路径,不然要运行的时候要报错 “is not known to the MapperRegistry”-->
  7. <insert id="insertUser" parameterType="User">
  8. insert into vincent_user(name,age) values(#{name},#{age})
  9. <!-- 这里sql结尾不能加分号,否则报“ORA-00911”的错误 -->
  10. </insert>
  11. <!-- 这里的id必须和UserMapper接口中的接口方法名相同,不然运行的时候也要报错 -->
  12. <select id="getUser" resultType="User" parameterType="java.lang.String">
  13. select * from vincent_user where name=#{name}
  14. </select>
  15. </mapper>

使用的测试类为:

[java] view plaincopy

  1. import org.apache.ibatis.session.SqlSession;
  2. import org.apache.ibatis.session.SqlSessionFactory;
  3. import org.junit.Test;
  4. public class TestMapper {
  5. static SqlSessionFactory sqlSessionFactory = null;
  6. static {
  7. sqlSessionFactory = MyBatisUtil.getSqlSessionFactory();
  8. }
  9. @Test
  10. public void testAdd() {
  11. SqlSession sqlSession = sqlSessionFactory.openSession();
  12. try {
  13. UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  14. User user = new User("tom",new Integer(5));
  15. userMapper.insertUser(user);
  16. sqlSession.commit();//这里一定要提交,不然数据进不去数据库中
  17. } finally {
  18. sqlSession.close();
  19. }
  20. }
  21. @Test
  22. public void getUser() {
  23. SqlSession sqlSession = sqlSessionFactory.openSession();
  24. try {
  25. UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  26. User user = userMapper.getUser("jun");
  27. System.out.println("name: "+user.getName()+"|age: "+user.getAge());
  28. } finally {
  29. sqlSession.close();
  30. }
  31. }
  32. }

2.整合Spring3后,单独使用Mybatis

首先,Spring3配置文件中(applicationContext.xml)有:

[html] view plaincopy

  1. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  2. <property name="configLocation" value="classpath:ibatis-config.xml" />
  3. <property name="dataSource" ref="dataSource" />
  4. <!-- mapper和resultmap配置路径 -->
  5. <property name="mapperLocations">
  6. <span style="white-space:pre">  </span><list>
  7. <span style="white-space:pre">  </span><value>classpath:com/log/bean/mapper/*.xml</value>
  8. <span style="white-space:pre">  </span></list>
  9. </property>
  10. </bean>

[html] view plaincopy

  1. <!-- 通过扫描的模式,扫描目录在com/log/bean/mapper目录下,所有的mapper都继承
  2. SQLMapper接口的接口, 这样一个bean就可以了 -->
  3. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  4. <property name="basePackage" value="com.log.bean.mapper"/>
  5. <property name="markerInterface" value="com.log.bean.mapper.SQLMapper"/>
  6. </bean>

工具类:

[java] view plaincopy

  1. import org.apache.ibatis.session.SqlSessionFactory;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.FileSystemXmlApplicationContext;
  4. public class MyBatisUtil  {
  5. private  final static SqlSessionFactory sqlSessionFactory;
  6. static {
  7. ApplicationContext ac = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
  8. sqlSessionFactory = (SqlSessionFactory)ac.getBean("sqlSessionFactory");
  9. }
  10. public static SqlSessionFactory getSqlSessionFactory() {
  11. return sqlSessionFactory;
  12. }
  13. }

myBatis3之从SqlSessionFactory中获取SqlSession

----------

现在,我们已经知道如何获取SqlSessionFactory对象了,基于同样的启示,我们就可以获得SqlSession的实例了。SqlSession对象完全包含以数据库为背景的所有执行SQL操作的方法。你可以用SqlSession实例来直接执行已映射的SQL 语句。例如:

Java代码  

  1. SqlSession session = sqlMapper.openSession();
  2. try{
  3. Blog blog = (Blog)session.selectOne("org.mybatis.example.BlogMapper.selectBlog",101);
  4. }finally{
  5. session.close();
  6. }

现在有一种更简洁的方法。使用合理描述参数和SQL语句返回值的接口(比如BlogMapper.class),这样现在就更简单,更安全的代码,没有容易发生的字符串文字和转换的错误。例如:

Java代码  

  1. SqlSession session = sqlSessionFactory.openSession();
  2. try {
  3. BlogMapper mapper = session.getMapper(BlogMapper.class);
  4. Blog blog = mapper.selectBlog(101);
  5. }finally{
  6. session.close();
  7. }

探究已映射的SQL语句

这里给出一个基于XML映射语句的示例,这些语句应该可以满足上述示例中SqlSession对象的调用。

Xml代码  

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4. <mapper namespace="org.mybatis.example.BlogMapper">
  5. <select id="selectBlog" parameterType="int" resultType="Blog">
  6. select * from Blog where id = #{id}
  7. </select>
  8. </mapper>

在命名空间“com.mybatis.example.BlogMapper”中,它定义了一个名为“selectBlog”的映射语句,这样它允许你使用完全限定名“org.mybatis.example.BlogMapper.selectBlog”来调用映射语句,我们下面示例中的写法也就是这样的。

Java代码  

  1. Blog blog = (Blog)session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);

但下面的调用更有优势:

映射接口对应映射xml文件的命令空间,接口方法对应映射xml文件中定义的SQL映射的ID。???????????

Java代码  

  1. BlogMapper mapper = session.getMapper(BlogMapper.class);
  2. Blog blog = mapper.selectBlog(101);

首先它不是基于文字的,那就更安全了。第二,如果你的IDE有代码补全功能,那么你可以利用它来操纵已映射的SQL语句。第三,不需要强制类型转换,同时BlogMapper接口可以保持简洁,返回值类型很安全(参数类型也很安全);

MyBatis 物理分页

MyBatis使用RowBounds实现的分页是逻辑分页,也就是先把数据记录全部查询出来,然在再根据offset和limit截断记录返回

为了在数据库层面上实现物理分页,又不改变原来MyBatis的函数逻辑,可以编写plugin截获MyBatis Executor的statementhandler,重写SQL来执行查询

参考资料: http://blog.csdn.net/hupanfeng/article/details/9265341

下面的插件代码只针对MySQL

plugin代码

package plugin;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
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.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;

/**
 * 通过拦截<code>StatementHandler</code>的<code>prepare</code>方法,重写sql语句实现物理分页。
 * 老规矩,签名里要拦截的类型只能是接口。
 *
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
public class PaginationInterceptor implements Interceptor {
    private static final Log logger = LogFactory.getLog(PaginationInterceptor.class);
    private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
    private static String DEFAULT_PAGE_SQL_ID = ".*Page$"; // 需要拦截的ID(正则匹配)

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY,
                DEFAULT_OBJECT_WRAPPER_FACTORY);
        RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");
        // 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次循环可以分离出最原始的的目标类)
        while (metaStatementHandler.hasGetter("h")) {
            Object object = metaStatementHandler.getValue("h");
            metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
        }
        // 分离最后一个代理对象的目标类
        while (metaStatementHandler.hasGetter("target")) {
            Object object = metaStatementHandler.getValue("target");
            metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
        }

        // property在mybatis settings文件内配置
        Configuration configuration = (Configuration) metaStatementHandler.getValue("delegate.configuration");

        // 设置pageSqlId
        String pageSqlId = configuration.getVariables().getProperty("pageSqlId");
        if (null == pageSqlId || "".equals(pageSqlId)) {
            logger.warn("Property pageSqlId is not setted,use default ‘.*Page$‘ ");
            pageSqlId = DEFAULT_PAGE_SQL_ID;
        }

        MappedStatement mappedStatement = (MappedStatement)
                metaStatementHandler.getValue("delegate.mappedStatement");
        // 只重写需要分页的sql语句。通过MappedStatement的ID匹配,默认重写以Page结尾的MappedStatement的sql
        if (mappedStatement.getId().matches(pageSqlId)) {
            BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
            Object parameterObject = boundSql.getParameterObject();
            if (parameterObject == null) {
                throw new NullPointerException("parameterObject is null!");
            } else {
                String sql = boundSql.getSql();
                // 重写sql
                String pageSql = sql + " LIMIT " + rowBounds.getOffset() + "," + rowBounds.getLimit();
                metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);
                // 采用物理分页后,就不需要mybatis的内存分页了,所以重置下面的两个参数
                metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
                metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
            }
        }
        // 将执行权交给下一个拦截器
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        // 当目标类是StatementHandler类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的次数
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
    }

    @Override
    public void setProperties(Properties properties) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

}

配置plugin

    <plugins>
        <plugin interceptor="plugin.PaginationInterceptor" />
    </plugins>

查询SQL

    <!-- 测试分页查询 -->
    <select id="selectUserByPage" resultMap="dao.base.userResultMap">
        <![CDATA[
        SELECT * FROM user
        ]]>
    </select>

调用示例

    @Override
    public List<User> selectUserByPage(int offset, int limit) {
        RowBounds rowBounds = new RowBounds(offset, limit);
        return getSqlSession().selectList("dao.userdao.selectUserByPage", new Object(), rowBounds);
    }

另外,结合Spring MVC,编写翻页和生成页码代码

页码类

package util;

/**
 * Created with IntelliJ IDEA.
 * User: zhenwei.liu
 * Date: 13-8-7
 * Time: 上午10:29
 * To change this template use File | Settings | File Templates.
 */
public class Pagination {
    private String url; // 页码url
    private int pageSize = 10;  // 每页显示记录数
    private int currentPage = 1;    // 当前页码
    private int maxPage = Integer.MAX_VALUE;    // 最大页数

    // 获取offset
    public int getOffset() {
        return (currentPage - 1) * pageSize;
    }

    // 获取limit
    public int getLimit() {
        return getPageSize();
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getCurrentPage() {
        return currentPage;
    }

    public void setCurrentPage(int currentPage) {
        if (currentPage < 1)
            currentPage = 1;
        if (currentPage > maxPage)
            currentPage = maxPage;
        this.currentPage = currentPage;
    }

    public int getMaxPage() {
        return maxPage;
    }

    public void setMaxPage(int maxPage) {
        this.maxPage = maxPage;
    }
}

为了计算最大页码,需要知道数据表的总记录数,查询SQL如下

    <!-- 记录总数 -->
    <select id="countUser" resultType="Integer">
        <![CDATA[
        SELECT COUNT(*) FROM user
        ]]>
    </select>

    @Override
    public Integer countTable() {
        return getSqlSession().selectOne("dao.userdao.countUser");
    }

Controller中的使用

    @RequestMapping("/getUserByPage")
    public String getUserByPage(@RequestParam
                                    int page, Model model) {
        pagination.setCurrentPage(page);
        pagination.setUrl(getCurrentUrl());
        pagination.setMaxPage(userDao.countTable() / pagination.getPageSize() + 1);
        List<User> userList = userDao.selectUserByPage(
                pagination.getOffset(), pagination.getLimit());
        model.addAttribute(pagination);
        model.addAttribute(userList);
        return "index";
    }

时间: 2024-08-19 10:52:50

Mybatis分页解决方案的相关文章

一个完整的Mybatis分页解决方案

[布景] 号码大全项目结构是 SpringMVC+Mybatis, 需求是想选用自定义的分页标签,关键词挖掘工具一起,要尽量少的影响事务程序开发的. 假如你已经运用了JS结构( 如:Ext,EasyUi等)自带的分页机能,这篇文章帮助能够不大,因为JS结构供给了固定的接口. [关于疑问] 大多数分页器会运用在查询页面,要思考以下疑问: 1)分页时是要随时带有近来一次查询条件 2)不能影响现有的sql,相似aop的作用 3)mybatis供给了通用的阻拦接口,要挑选恰当的阻拦办法和时点 4)尽量少

很好用的mybatis分页解决方案

分页如果写在SQL脚本中,将会大大影响我们后续数据库的迁移难度.mybatis的分页一般是自己实现一个mybatis的拦截器,然后根据某些特定的条件开启分页,对原有SQL进行改造. 正在我对mybatis的拦截器进行研究的时候从网上找到了一个很好的分页插件,主页地址是 https://github.com/pagehelper/Mybatis-PageHelper 已经实现了拦截器/多种数据库的适配 基本上很方便的即可集成到我们的项目中.集成方式: 1.使用maven引入jar包: <depen

分享一个完整的Mybatis分页解决方案

Mybatis 的物理分页是应用中的一个难点,特别是配合检索和排序功能叠加时更是如此. 我在最近的项目中开发了这个通用分页器,过程中参考了站内不少好文章,阅读源码帮助更大minglisoft.cn/technology [背景] 项目框架是 SpringMVC+Mybatis, 需求是想采用自定义的分页标签,同时,要尽量少的影响业务程序开发的.如果你已经使用了JS框架( 如:Ext,EasyUi等)自带的分页机能,是属于前端分页,不在本文讨论范围. [关于问题] 大多数分页器会使用在查询页面,要

Mybatis分页-利用Mybatis Generator插件生成基于数据库方言的分页语句,统计记录总数 (转)

众所周知,Mybatis本身没有提供基于数据库方言的分页功能,而是基于JDBC的游标分页,很容易出现性能问题.网上有很多分页的解决方案,不外乎是基于Mybatis本机的插件机制,通过拦截Sql做分页.但是在像Oracle这样的数据库上,拦截器生成的Sql语句没有变量绑定,而且每次语句的都要去拦截,感觉有点浪费性能. Mybatis Generator是Mybatis的代码生成工具,可以生成大部分的查询语句. 本文提供的分页解决方案是新增Mybatis Generator插件,在用Mybatis

springboot +mybatis分页插件PageHelper

1.问题描述 JAVA界ORM的两位大佬Hibernate和Mybatis,hb自带分页(上手挺快,以前用了好几年hb,后期运维及优化快疯了),mybatis没有分页功能,需要借助第三方插件来完成,比较流行的三方框架:PageHelper,今天结合springboot做下介绍,直接贴线上配置,保证可用(如有遗漏,朋友们可以指正下). 2. 解决方案 2.1 配置项目pom.xml <!--分页--> <dependency> <groupId>com.github.pa

mybatis分页和主键生成

oracle 使用 mybatis的时候,主键自动生成: <insert id="createBigOrder" parameterType="BigOrder" useGeneratedKeys="true" keyProperty="id"> <selectKey resultType="int" order="BEFORE" keyProperty="i

Mybatis分页插件

Mybatis分页插件 - PageHelper说明 如果你也在用Mybatis,建议尝试该分页插件,这个一定是最方便使用的分页插件. 该插件目前支持Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六种数据库分页. 点击提交BUG 版本说明 最新版本为3.7.5 PageInfo中的judgePageBoudary方法修改: isLastPage = pageNum == pages && pageNum != 1; //改为 isLastPage

大数据分页解决方案

1 编写目的 解决系统需要检索大数据列表时的性能问题,而提出的分页方案 2 术语.定义和缩略语 3 大数据量检索的性能问题分析 大数据量检索的性能存在问题,问题主要包括 3.1 客户端在IE或者cs端数据量过大会导致IE变慢,甚至死锁 现象: IE浏览器崩溃 浏览器白板,停止响应 3.2 客户端--web服务器之间的数据传输量大会导致客户端速度变慢,效率降低 现象: IE浏览器长期在等待时白板 IE 浏览器操作慢 3.3 中间层构造大数据列表会导致中间层性能降低 现象: 并发访问多时,应用服务器

Mybatis分页插件2.0版本发布

项目地址:http://git.oschina.net/free/Mybatis_PageHelper 分页插件示例: http://blog.csdn.net/isea533/article/details/24700339 v2.0更新内容: 支持Mybatis缓存,count和分页同时支持(二者同步) 修改拦截器签名,拦截Executor,签名如下: @Intercepts(@Signature(type = Executor.class, method = "query", a