美文网首页
MyBatis原理详解

MyBatis原理详解

作者: 太大_453b | 来源:发表于2018-04-25 16:30 被阅读205次

    前言

    MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简单、优雅。本文主要讲述MyBatis的架构设计思路,并且讨论MyBatis的几个核心部件,然后结合一个select查询实例,深入代码,来探究MyBatis的实现

    接口层---和数据库交互的方式

    MyBatis和数据库的交互有两种方式
    • 使用传统的MyBatis提供的API
    • 使用Mapper接口

    使用传统的MyBatis提供的API

    sqlSession.selectList("statmentid", params)
    

    使用Mapper接口

    通过interface的方法和mapper.xml的statmentid对应来执行对应的方法
    @Mapper
    public interface UserMapper{
        void say();
    }
    Mapper.xml
    <select id = "say" paramerterType="map">
        select * from table
    </select>
    

    数据处理层

    数据处理层可以说是MyBatis 的核心

    • 通过传入参数构建动态SQL语句
    • SQL语句的执行以及封装查询结果集成List<E>

    框架支撑层

    1. 事务管理机制
    2. 连接池管理机制
    3. 缓存机制

    分为一级缓存和二级缓存,一级缓存默认开启,二期缓存默认关闭, 一级缓存为Mapper私有,二级则为共享;二级缓存在数据更新后会全部清空在重新更新缓存,效率一般

    1. SQL语句的配置方式()

    xml的配置方式,都比较熟悉,不做介绍

    引导层

    引导层是配置和启动MyBatis 配置信息的方式。MyBatis 提供两种方式来引导MyBatis :基于XML配置文件的方式和基于Java API 的方式

    MyBatis的主要构件及其相互关系

    • Sqlsession 主要构件,数据库会话,完成增删改查等功能
    • Excuteor 执行器
    • StatmentHandler 封装了JDBC Statment操作,如设置参数,将结果集转换成List
    • ParameterHandler 传参封装了JDBC Statment操作
    • ResultSetHandler 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
    • TypeHandler 负责java数据类型和jdbc数据类型之间的映射和转换
    • MappedStatment MappedStatement 维护了一条<select|update|delete|insert>节点的封装
    • SqlSource 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
    • BoundSql 表示动态生成的SQL语句以及相应的参数信息
    • Configuration MyBatis所有的配置信息都维持在Configuration对象之中

    Mybatis架构设计

    1. SqlSession的工作分析
    //第一步获取session
    SqlSession sqlSession = factory.openSession();
    //执行
    List<Employee> result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",params); 
    

    selectList源码

    public <E> List<E> selectList(String statement, Object parameter) {  
      return this.selectList(statement, parameter, RowBounds.DEFAULT);  
    }  
      
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {  
      try {  
        //1.根据Statement Id,在mybatis 配置对象Configuration中查找和配置文件相对应的MappedStatement      
        MappedStatement ms = configuration.getMappedStatement(statement);  
        //2. 将查询任务委托给MyBatis 的执行器 Executor  
        List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);  
        return result;  
      } catch (Exception e) {  
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);  
      } finally {  
        ErrorContext.instance().reset();  
      }  
    }  
    

    然后看query方法

    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {  
            
        // 1.根据具体传入的参数,动态地生成需要执行的SQL语句,用BoundSql对象表示    
        BoundSql boundSql = ms.getBoundSql(parameter);  
        // 2.为当前的查询创建一个缓存Key  
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);  
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);  
     }  
      
      @SuppressWarnings("unchecked")  
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {  
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());  
        if (closed) throw new ExecutorException("Executor was closed.");  
        if (queryStack == 0 && ms.isFlushCacheRequired()) {  
          clearLocalCache();  
        }  
        List<E> list;  
        try {  
          queryStack++;  
          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;  
          if (list != null) {  
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);  
          } else {  
            // 3.缓存中没有值,直接从数据库中读取数据    
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);  
          }  
        } finally {  
          queryStack--;  
        }  
        if (queryStack == 0) {  
          for (DeferredLoad deferredLoad : deferredLoads) {  
            deferredLoad.load();  
          }  
          deferredLoads.clear(); // issue #601  
          if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {  
            clearLocalCache(); // issue #482  
          }  
        }  
        return list;  
      }  
     private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {  
        List<E> list;  
        localCache.putObject(key, EXECUTION_PLACEHOLDER);  
        try {  
              
          //4. 执行查询,返回List 结果,然后    将查询的结果放入缓存之中  
          list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);  
        } finally {  
          localCache.removeObject(key);  
        }  
        localCache.putObject(key, list);  
        if (ms.getStatementType() == StatementType.CALLABLE) {  
          localOutputParameterCache.putObject(key, parameter);  
        }  
        return list;  
      }  
    

    然后是doQuery

    /** 
    * 
    *SimpleExecutor类的doQuery()方法实现 
    * 
    */  
      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {  
        Statement stmt = null;  
        try {  
          Configuration configuration = ms.getConfiguration();  
          //5. 根据既有的参数,创建StatementHandler对象来执行查询操作  
          StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);  
          //6. 创建java.Sql.Statement对象,传递给StatementHandler对象  
          stmt = prepareStatement(handler, ms.getStatementLog());  
          //7. 调用StatementHandler.query()方法,返回List结果集  
          return handler.<E>query(stmt, resultHandler);  
        } finally {  
          closeStatement(stmt);  
        }  
      }  
      //prepareStatment的实现
      private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {  
        Statement stmt;  
        Connection connection = getConnection(statementLog);  
        stmt = handler.prepare(connection);  
        //对创建的Statement对象设置参数,即设置SQL 语句中 ? 设置为指定的参数  
        handler.parameterize(stmt);  
        return stmt;  
      }  
    

    剩下的基本就不用看了。
    总结来说可以这样理解

    1. 创建一个sqlsession会话
    2. 通过配置的xml文件找到对应的sql,然后通过参数和sql动态的生成一条sql
    3. 最后执行封装结果集

    在动态生产sql的过程还是用了JDBC的Statment本质上都是一样的,只不过这些myatis全部给你封装好了

    相关文章

      网友评论

          本文标题:MyBatis原理详解

          本文链接:https://www.haomeiwen.com/subject/theylftx.html