干翻Mybatis源码系列之第十二篇:自写Mybatis拦截器实现分页操作

简介: 干翻Mybatis源码系列之第十二篇:自写Mybatis拦截器实现分页操作

给自己的每日一句

不从恶人的计谋,不站罪人的道路,不坐亵慢人的座位,惟喜爱耶和华的律法,昼夜思想,这人便为有福!他要像一棵树栽在溪水旁,按时候结果子,叶子也不枯干。凡他所做的尽都顺利。

如何找到孙帅本人

本文内容整理自《孙哥说Mybatis系列视频课程》,老师实力十分雄厚,B站搜孙帅可以找到本人,视频中有老师的微信号。

前言

Mybatis当中可以处理通用的分页,Mybatis开发中传统的分页是怎么接入Mybatis呢?

Mybatis传统开发就:entity、别名、建表、Dao、Mapper、注册、API编程,这七步骤。

Mybatis的分页显然是在Dao和Mapper设计上体现的。在Dao中我们如果引入分页的话,那么就得有页码和每页显示数量了。

UserDao{
  List<User> queryAllUsers(int pageIndex,int number);
  List<User> queryUsersByName(int pageIndex,int number,String name);
}
UserDaoMapper.xml
<select id = "queryAllUsers">
  select * from t_user limit pageindex-1,5<>
</select>
<select id = "queryUsersByname">
  select * from t_user where name like #{name} limit pageindex-1,5
</select>

如果这么玩的话,这个Mapper.xml文件中的Dao层分页查询,得在所有的select标签当中进行添加,这么操作十分冗余。冗余还不是最可怕的,最可怕的是修改起来,那真的是要了命了,假如我们切换了Oracle的数据源,那么select标签中的limit可就不好使了!这样的代码就废了

所以这么干的话,SQL就与厂商特性耦合了。这一块JPA是解决的非常好的,他是通过Dialect方言解决的。

一:分页处理集中处理

解决冗余和解决分页厂商方言问题。这样就适用于我们的拦截器了。我们直接在拦截器当中加上limit 操作即可。

二:不太完善版

@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class PageHelperInterceptor1 extends MyMybatisInterceptorAdapter {
    private static final Logger log = LoggerFactory.getLogger(PageHelperInterceptor1.class);
    private String queryMethodPrefix;
    private String queryMethodSuffix;
    private String databaseType;
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if (log.isInfoEnabled())
            log.info("----pageHelperInterceptor------");
        //获得sql语句 拼接字符串 limit
        MetaObject metaObject = SystemMetaObject.forObject(invocation);
        String sql = (String) metaObject.getValue("target.delegate.boundSql.sql");
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("target.delegate.mappedStatement");
        String id = mappedStatement.getId();
    //基于拦截器propertis灵活掌握是否分页的手段。
        if (id.indexOf(queryMethodPrefix) != -1 && id.endsWith(queryMethodSuffix)) {
            //分页相关的操作封装 对象(vo dto)
            //获得Page对象 并设置Page对象 totalSize属性 算出总页数
            //假设 Page
            //Page page = new Page(1);
            //直接通过DAO方法的参数 获得Page对象
            //Page page = (Page) metaObject.getValue("target.delegate.parameterHandler.parameterObject");
            //通过ThreadLocalUtils
            Page page = ThreadLocalUtils.get();
            //清空一下
            //select id,name from t_user 获得 全表有多少条数据
            // select count(*) from t_user
            //select id,name from t_user where name = ?;
            //select count(*)fromt t_user where name = ?
            //select id,name from t_user where  name = ? and id = ?;
            String countSql = "select count(*) " + sql.substring(sql.indexOf("from"));
            //JDBC操作
            //1 Connection  PreapredStatement
            Connection conn = (Connection) invocation.getArgs()[0];
            PreparedStatement preparedStatement = conn.prepareStatement(countSql);
           /* preparedStatement.setString(1,?)
            preparedStatement.setString(2,?);*/
            //这里是什么操作呢? 
            //Mybatis当中ParameterHandler提供的处理参数的方法。自动取这个值,自动取组装值即可。
            ParameterHandler parameterHandler = (ParameterHandler) metaObject.getValue("target.delegate.parameterHandler");
            parameterHandler.setParameters(preparedStatement);
9jh
            ResultSet resultSet = preparedStatement.executeQuery();
            if(resultSet.next()){
               page.setTotalSize(resultSet.getInt(1));
            }
            //page.setTotalSize();
  `            //做一个判断 如果当前是MySQL 如果是Oracle....
            //if databaseType == "oracle" or "mysql"
            String newSql = sql + " limit "+page.getFirstItem()+","+page.getPageCount();
            metaObject.setValue("target.delegate.boundSql.sql", newSql);
        }
        return invocation.proceed();
    }
    @Override
    public void setProperties(Properties properties) {
        this.queryMethodPrefix = properties.getProperty("queryMethodPrefix");
        this.queryMethodSuffix = properties.getProperty("queryMethodSuffix");
    }
}
/**
 * 目的 封装分页相关操作
 * pageIndex 页号
 * web 前端传递
 * pageCount 每页显示多少条
 * 写死 3
 * totalSize 总共有多少条
 * count(*) 算出来
 * <p>
 * *    pageSize  一共有多少页
 * totalSize / pageCount
 * 6 / 3 = 2
 * 7 / 3 = 3
 */
public class Page {
    private Integer pageIndex;
    private Integer pageCount;
    //查询数据库的
    //sql语句相关
    //计算在哪里完成? 拦截器操作
    private Integer totalSize;
    private Integer pageSize;
    public Page(Integer pageIndex) {
        this.pageIndex = pageIndex;
        this.pageCount = 5;
    }
    public Page(Integer pageIndex, Integer pageCount) {
        this.pageIndex = pageIndex;
        this.pageCount = pageCount;
    }
    public Integer getPageIndex() {
        return pageIndex;
    }
    public void setPageIndex(Integer pageIndex) {
        this.pageIndex = pageIndex;
    }
    public Integer getPageCount() {
        return pageCount;
    }
    public void setPageCount(Integer pageCount) {
        this.pageCount = pageCount;
    }
    public Integer getTotalSize() {
        return totalSize;
    }
    public void setTotalSize(Integer totalSize) {
        this.totalSize = totalSize;
        if (totalSize % pageCount == 0) {
            this.pageSize = totalSize / pageCount;
        } else {
            this.pageSize = totalSize / pageCount + 1;
        }
    }
    public Integer getPageSize() {
        return pageSize;
    }
    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }
    // limit getFirstItem,pageSize;
    public Integer getFirstItem() {
        return pageIndex - 1;
    }
}
@Test
    public void test2() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        SqlSession sqlSession3 = sqlSessionFactory.openSession();
        UserDAO userDAO1 = sqlSession1.getMapper(UserDAO.class);
        UserDAO userDAO2 = sqlSession2.getMapper(UserDAO.class);
        UserDAO userDAO3 = sqlSession3.getMapper(UserDAO.class);
       /* List<User> users1 = userDAO1.queryAllUsersByPage();//--- ms ---> cache
        for (User user : users1) {
            System.out.println("user = " + user);
        }*/
        User user1 = userDAO1.queryUserById(4); //---ms ---> cache
        sqlSession1.commit();
        //userDAO1.queryAllUsers();
        System.out.println("-----------------------------------------");
        /*List<User> users2 = userDAO2.queryAllUsersByPage();
        for (User user : users2) {
            System.out.println("user = " + user);
        }*/
        sqlSession2.commit();
        System.out.println("---------------------------------2人
        --------");
//
 /*       User user = new User(4, "xiaowb");
        userDAO3.update(user);
        sqlSession3.commit();*/
    }

三:如何获取Page对象

如何获取Page对象的获取和封装,这个位置应该发生在什么位置呢?应该在Controller层当中。服务器解析请求数据的时候会在Controller当中进行解析。controller会通过Request对象获取到页号。并在Controller当中封装成page对象。把Page对象放置到ThreadLocal当中一直送到Dao当中,也可以直接在Dao层当中基于参数传递过来即可。

直接使用ThreadLocal会更好。

1:Tomcat处理一个请求

Tomcat接收到一个请求之后,会从线程池大当中去取出来一个线程来处理请求。请求完毕之后会将线程放回到线程池当中。我们把Page对象放到线程的ThreadLocal当中即可。

ThreadLocal是sun公司为我们提供的一个工具,把一个对象存储在当前线程中。可以准确的获取当前线程,也可以知道对象存储在线程的什么位置。ThreadLocal在Thread当中以自己作为Key,存储在Map中。

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

ThreadLocal就是操作了当前Thread当中的Map,ThreadLocal对象本身作为Map的key,value是存储在其中的值,神奇的是,Thread中的Map是定义在ThreadLocal当中的static class.

2:问题集锦

1):ThreadLocal存在内存泄露

内存泄露核心就是,你所存储的东西太多了,导致你的内存不够用了,所以Dao用完了,我得把Map中的数据给清掉。用完之后就把他干掉。

2):ThreadLocal到底把数据存储在了哪里?

Thread当中。

Connection、SqlSession、Page这些对象经常会存储到Thread当中。

3):什么时候调用ThreadLocal把Page存储在线程中?

Controller当中。当然,我们认为这个还不够早,我们觉得在filter过滤器当中就可以执行这个操作了。

四:Mybatis拦截器中操作Sql的工具?

1:SQLparser解析器

/**
     * 用于测试:jSqlparser
     */
    @Test
    public void testSQLParser() throws JSQLParserException {
        CCJSqlParserManager parserManager = new CCJSqlParserManager();
        Select select = (Select) parserManager.parse(new StringReader("select id,name from t_user where name = 'suns' "));
        PlainSelect selectBody = (PlainSelect) select.getSelectBody();
        //FromItem table = selectBody.getFromItem();
        //System.out.println("table = " + table);
     /*   Expression where = selectBody.getWhere();
        System.out.println("where = " + where);*/
       /* List<SelectItem> selectItems = selectBody.getSelectItems();
        for (SelectItem selectItem : selectItems) {
            System.out.println("selectItem = " + selectItem);
        }*/
    }
@Test
    public void testSQLParser1() throws JSQLParserException {
        CCJSqlParserManager parserManager = new CCJSqlParserManager();
        Update update = (Update) parserManager.parse(new StringReader("update t_user set name='suns',password='12345' where id=1 "));
        /*Table table = update.getTable();
        System.out.println("table = " + table);*/
        List<Column> columns = update.getColumns();
        for (Column column : columns) {
            System.out.println(column);
        }
        List<Expression> expressions = update.getExpressions();
        for (Expression expression : expressions) {
            System.out.println(expression);
        }
    }
相关文章
|
5天前
|
SQL 存储 算法
Mybatis-Plus- CRUD接口-主键策略-自动填充和乐观锁-分页-逻辑删除-条件构造器和常用接口
Mybatis-Plus- CRUD接口-主键策略-自动填充和乐观锁-分页-逻辑删除-条件构造器和常用接口
|
6天前
|
SQL Java 数据库连接
【JavaEE】懒人的福音-MyBatis框架—复杂的操作-动态SQL(下)
【JavaEE】懒人的福音-MyBatis框架—复杂的操作-动态
6 0
|
6天前
|
SQL Java 数据库连接
【JavaEE】懒人的福音-MyBatis框架—复杂的操作-动态SQL(上)
【JavaEE】懒人的福音-MyBatis框架—复杂的操作-动态SQL
5 0
|
6天前
|
SQL Java 数据库连接
【JavaEE】懒人的福音-MyBatis框架—[单表]增删改查等常规操作(下)
【JavaEE】懒人的福音-MyBatis框架—[单表]增删改查等常规操作
8 0
|
6天前
|
SQL 前端开发 Java
【JavaEE】懒人的福音-MyBatis框架—[单表]增删改查等常规操作(上)
【JavaEE】懒人的福音-MyBatis框架—[单表]增删改查等常规操作
8 0
|
6天前
|
SQL Java 数据库连接
MyBatis 初识简单操作
MyBatis 初识简单操作
14 0
|
6天前
|
SQL 监控 Java
mybatis拦截器实现
mybatis拦截器实现
14 0
|
6天前
|
SQL Java 数据库连接
一文细说Mybatis八大核心源码
以上 是V哥给大家整理的8大核心组件的全部内容,为什么说选择 Java 就是选择未来,真正爱 Java 的人,一定喜欢深入研究,学习源码只是第一步,要有一杆子捅到操作系统才够刺激。
|
6天前
|
SQL 前端开发 Java
通过使用Mybatis插件来实现数据的分页功能
通过使用Mybatis插件来实现数据的分页功能
|
6天前
|
SQL 缓存 Java