• Mybatis源码分析


    MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。如何新建MyBatis源码工程请点击MyBatis源码分析-IDEA新建MyBatis源码工程

      MyBatis框架主要完成的是以下2件事情:

    1. 根据JDBC规范建立与数据库的连接。
    2. 通过反射打通Java对象与数据库参数交互之间相互转换的关系。

      MyBatis框架是一种典型的交互式框架,先准备好交互的必要条件,然后构建一个交互的环境,在交互环境中划分会话,在会话中与数据库进行交互数据。

    1 MyBatis主要的类

    • Configuration        MyBatis所有的配置信息都维持在Configuration对象之中。
    • SqlSession            作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能
    • Executor               MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
    • StatementHandler 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。
    • ParameterHandler  负责对用户传递的参数转换成JDBC Statement 所需要的参数,
    • ResultSetHandler   负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
    • TypeHandler          负责java数据类型和jdbc数据类型之间的映射和转换
    • MappedStatement  MappedStatement维护了一条<select|update|delete|insert>节点的封装,
    • SqlSource              负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
    • BoundSql              表示动态生成的SQL语句以及相应的参数信息

      以上几个类在SQL操作中都会涉及,在SQL操作中重点关注下SQL参数什么时候写入和结果集怎么转换为Java对象,这两个过程正好对应的类是PreparedStatementHandler和ResultSetHandler类。

    (图片来自《深入理解mybatis原理》 MyBatis的架构设计以及实例分析)

    2 SQL执行流程

      MyBatis主要设计目的还是为了让我们在执行SQL时对输入输出的数据的管理更加方便,所以方便的让我们写出SQL和方便的获取SQL的执行结果是MyBatis的核心竞争力。下面就用一个例子来从源码角度看一下SQL的完整执行流程。

    新建配置文件conf.xml:

    复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
    
    <configuration>
    
        <settings>
            <setting name="cacheEnabled" value="true"/>
            <setting name="lazyLoadingEnabled" value="false"/>
            <!--setting name="logImpl" value="STDOUT_LOGGING"/--> <!-- 日志 -->
        </settings>
    
        <typeAliases>
            <typeAlias type="com.luoxn28.dao.User" alias="User"/>
        </typeAliases>
    
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC" /> <!-- 声明使用那种事务管理机制 JDBC/MANAGED -->
                <!-- 配置数据库连接信息 -->
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver" />
                    <property name="url" value="jdbc:mysql://192.168.1.150:3306/xxx" />
                    <property name="username" value="xxx" />
                    <property name="password" value="xxx" />
                </dataSource>
            </environment>
        </environments>
    
        <mappers>
            <mapper resource="userMapper.xml"/>
        </mappers>
    
    </configuration>
    复制代码

    首先建立数据表,这里就以user表为例 :

    复制代码
    DROP TABLE IF EXISTS user;
    CREATE TABLE user (
      id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
      name VARCHAR(32) NOT NULL,
      password VARCHAR(32) NOT NULL,
      sex int,
      email VARCHAR(32),
      phone VARCHAR(16),
      admin VARCHAR(16)
    );
    复制代码

    然后新建与数据表对应的类User:

     User

    再新建usre表的配置文件:

     userMapper.xml

    最后新建测试类:

    复制代码
    /**
     * MyBatis测试类
     */
    public class TestMain {
    
        public static void main(String[] args) throws IOException {
            String resouce = "conf.xml";
            InputStream is = Resources.getResourceAsStream(resouce);
    
            // 构建sqlSession工厂
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
                    // 获取sqlSession
            SqlSession session = sqlSessionFactory.openSession();
    
            User user;
    
            try {
                /**
                 * 第一种方式: 直接执行已映射的 SQL 语句
                 */
                String statement = "com.luoxn28.dao.UserDao.getById";
                user = session.selectOne(statement, 1);
                System.out.println(user);
            }
            finally {
                session.close();
            }
    
            /**
             * 第二种方式: 执行更清晰和类型安全的代码
             */
    //        UserDao userDao = session.getMapper(UserDao.class);
    //        user = userDao.getById(1);
    //        System.out.println(user);
        }
    
    }
    复制代码

      由于我们分析的是SQL的执行流程,那就重点关注下 user = session.selectOne(statement, 1); 这行代码~ 注意,传进去的参数是1。

      session是DefaultSqlSession类型的,因为sqlSessionFactory默认生成的SqlSession是DefaultSqlSession类型。selectOne()会调用selectList()。

    复制代码
    // DefaultSqlSession类
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
            MappedStatement ms = configuration.getMappedStatement(statement);
            // CURD操作是交给Excetor去处理的
            return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
            ErrorContext.instance().reset();
        }
    }
    复制代码

      在DefaultSqlSession.selectList中的各种CURD操作都是通多Executor进行的,这里executor的类型是CachingExecutor,接着跳转到其中的query方法中。

    复制代码
    // CachingExecutor 类
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        BoundSql boundSql = ms.getBoundSql(parameterObject);  // 获取绑定的sql命令,比如"SELECT * FROM xxx"
        CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
        return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
    复制代码

      getBoundSql为了获取绑定的sql命令,在创建完cacheKey之后,就进入到CachingExecutor 类中的另一个query方法中。

    复制代码
    // CachingExecutor 类
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
            throws SQLException {
        Cache cache = ms.getCache();
        if (cache != null) {
            flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
                ensureNoOutParams(ms, parameterObject, boundSql);
                @SuppressWarnings("unchecked")
                List<E> list = (List<E>) tcm.getObject(cache, key);
                if (list == null) {
                    list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                    tcm.putObject(cache, key, list); // issue #578 and #116
                }
                return list;
            }
        }
        return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
    复制代码

      这里真正执行query操作的是SimplyExecutor代理来完成的,接着就进入到了SimplyExecutor的父类BaseExecutor的query方法中。

    复制代码
    // SimplyExecutor的父类BaseExecutor类
    @SuppressWarnings("unchecked")
    @Override
    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++;
            /**
             * localCache是一级缓存,如果找不到就调用queryFromDatabase从数据库中查找
             */
            list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
            if (list != null) {
                handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            } else {
                list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            queryStack--;
        }
        if (queryStack == 0) {
            for (DeferredLoad deferredLoad : deferredLoads) {
                deferredLoad.load();
            }
            // issue #601
            deferredLoads.clear();
            if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                // issue #482
                clearLocalCache();
            }
        }
        return list;
    }
    复制代码

      因为是第一次SQL查询操作,所以会调用queryFromDatabase方法来执行查询。

    复制代码
    // SimplyExecutor的父类BaseExecutor类
    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 {
            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;
    }
    复制代码

      从数据库中查询数据,进入到SimplyExecutor中进行操作。

    复制代码
    // SimplyExecutor类
    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();
            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            // 子流程1:SQL查询参数的设置
            stmt = prepareStatement(handler, ms.getStatementLog());
            // StatementHandler封装了Statement
            // 子流程2:SQL查询操作和结果集的封装
            return handler.<E>query(stmt);
        } finally {
            closeStatement(stmt);
        }
    }
    复制代码

      注意,在prepareStatement方法中会进行SQL查询参数的设置,也就是咱们最开始传递进来的参数,其值为1。handler.<E>query(stmt)方法中会进行实际的SQL查询操作和结果集的封装(封装成Java对象)。当流程走到这里时,程序已经压栈有一定深度了,因为接下来程序分析会兵分两路,一方面深入到SQL查询及结果集的设置子流程1中,然后再深入到SQL查询操作和结果集的封装子流程2,因为还会回到这里,所以就来一张调用栈的特写吧:

    子流程1:SQL查询参数的设置

    复制代码
    // SimplyExecutor类
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        // 获取一个Connection
        Connection connection = getConnection(statementLog);
        stmt = handler.prepare(connection, transaction.getTimeout());
        handler.parameterize(stmt); // 设置SQL查询中的参数值
        return stmt;
    }
    复制代码

      通过getConnection方法来获取一个Connection,调用prepare方法来获取一个Statement(这里的handler类型是RoutingStatementHandler,RoutingStatementHandler的prepare方法调用的是PrepareStatementHandler的prepare方法,因为PrepareStatementHandler并没有覆盖其父类的prepare方法,其实最后调用的是BaseStatementHandler中的prepare方法。是不是绕晕了,那就再看一遍吧 :) )。调用parameterize方法来设置SQL的参数值(这里最后调用的是PrepareStatementHandler中的parameterize方法,而PrepareStatementHandler.parameterize方法调用的是DefaultParameterHandler中的setParameters方法)。

    // PrepareStatementHandler类
    @Override
    public void parameterize(Statement statement) throws SQLException {
        parameterHandler.setParameters((PreparedStatement) statement);
    }
    复制代码
    // DefaultParameterHandler类
    @Override
    public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings != null) {
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    Object value;
                    String propertyName = parameterMapping.getProperty();
                    if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                        value = boundSql.getAdditionalParameter(propertyName);
                    } else if (parameterObject == null) {
                        value = null;
                    } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                        value = parameterObject;
                    } else {
                        MetaObject metaObject = configuration.newMetaObject(parameterObject);
                        value = metaObject.getValue(propertyName);
                    }
                    TypeHandler typeHandler = parameterMapping.getTypeHandler();
                    JdbcType jdbcType = parameterMapping.getJdbcType();
                    if (value == null && jdbcType == null) {
                        jdbcType = configuration.getJdbcTypeForNull();
                    }
                    try {
                        typeHandler.setParameter(ps, i + 1, value, jdbcType);
                    } catch (TypeException e) {
                        throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                    } catch (SQLException e) {
                        throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                    }
                }
            }
        }
    }
    复制代码

      到这里为止,已经给Statement设置了最初传递进去的参数(值为1)了,那么接着分析流程2:

    流程2:SQL查询及结果集的设置

    // RoutingStatementHandler类
    @Override
    public <E> List<E> query(Statement statement) throws SQLException {
        return delegate.<E>query(statement);
    }
    复制代码
    // RoutingStatementHandler类
    @Override
    public <E> List<E> query(Statement statement) throws SQLException {
        // 这里就到了熟悉的PreparedStatement了
        PreparedStatement ps = (PreparedStatement) statement;
        // 执行SQL查询操作
        ps.execute();
        // 结果交给ResultHandler来处理
        return resultSetHandler.<E> handleResultSets(ps);
    }
    复制代码
    复制代码
    // DefaultResultSetHandler类(封装返回值,将查询结果封装成Object对象)
    @Override
    public List<Object> handleResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
    
        final List<Object> multipleResults = new ArrayList<Object>();
    
        int resultSetCount = 0;
        ResultSetWrapper rsw = getFirstResultSet(stmt);
    
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        validateResultMapsCount(rsw, resultMapCount);
        while (rsw != null && resultMapCount > resultSetCount) {
            ResultMap resultMap = resultMaps.get(resultSetCount);
            handleResultSet(rsw, resultMap, multipleResults, null);
            rsw = getNextResultSet(stmt);
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
        }
    
        String[] resultSets = mappedStatement.getResultSets();
        if (resultSets != null) {
            while (rsw != null && resultSetCount < resultSets.length) {
                ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
                if (parentMapping != null) {
                    String nestedResultMapId = parentMapping.getNestedResultMapId();
                    ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
                    handleResultSet(rsw, resultMap, null, parentMapping);
                }
                rsw = getNextResultSet(stmt);
                cleanUpAfterHandlingResultSet();
                resultSetCount++;
            }
        }
    
        return collapseSingleResultList(multipleResults);
    }
    复制代码

      ResultSetWrapper是ResultSet的包装类,调用getFirstResultSet方法获取第一个ResultSet,同时获取数据库的MetaData数据,包括数据表列名、列的类型、类序号等,这些信息都存储在ResultSetWrapper类中了。然后调用handleResultSet方法来来进行结果集的封装。

    复制代码
    // DefaultResultSetHandler类
    private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
        try {
            if (parentMapping != null) {
                handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
            } else {
                if (resultHandler == null) {
                    DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
                    handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
                    multipleResults.add(defaultResultHandler.getResultList());
                } else {
                    handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
                }
            }
        } finally {
            // issue #228 (close resultsets)
            closeResultSet(rsw.getResultSet());
        }
    }
    复制代码

      这里调用handleRowValues方法来进行值的设置:

    复制代码
    // DefaultResultSetHandler类
    public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        if (resultMap.hasNestedResultMaps()) {
            ensureNoRowBounds();
            checkResultHandler();
            handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        } else {
            // 封装数据
            handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        }
    }
    复制代码
    复制代码
    // DefaultResultSetHandler类
    // 封装数据
    private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
            throws SQLException {
        DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
        skipRows(rsw.getResultSet(), rowBounds);
        while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
            ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
            Object rowValue = getRowValue(rsw, discriminatedResultMap);
            storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
        }
    }
    复制代码
    复制代码
    // DefaultResultSetHandler类
    private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
        final ResultLoaderMap lazyLoader = new ResultLoaderMap();
        // createResultObject为新创建的对象,数据表对应的类
        Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
        if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
            final MetaObject metaObject = configuration.newMetaObject(resultObject);
            boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
            if (shouldApplyAutomaticMappings(resultMap, false)) {
                // 这里把数据填充进去,metaObject中包含了resultObject信息
                foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
            }
            foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
            foundValues = lazyLoader.size() > 0 || foundValues;
            resultObject = foundValues ? resultObject : null;
            return resultObject;
        }
        return resultObject;
    }
    复制代码
    复制代码
    // DefaultResultSetHandler类(把ResultSet中查询结果填充到JavaBean中)
    private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
        List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
        boolean foundValues = false;
        if (autoMapping.size() > 0) {
                    // 这里进行for循环调用,因为user表中总共有7项,所以也就调用7次
            for (UnMappedColumnAutoMapping mapping : autoMapping) {
                // 这里将esultSet中查询结果转换为对应的实际类型
                final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
                if (value != null) {
                    foundValues = true;
                }
                if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
                    // gcode issue #377, call setter on nulls (value is not 'found')
                    metaObject.setValue(mapping.property, value);
                }
            }
        }
        return foundValues;
    }
    复制代码

      mapping.typeHandler.getResult会获取查询结果值的实际类型,比如我们user表中id字段为int类型,那么它就对应Java中的Integer类型,然后通过调用statement.getInt("id")来获取其int值,其类型为Integer。metaObject.setValue方法会把获取到的Integer值设置到Java类中的对应字段。

    复制代码
    // MetaObject类
    public void setValue(String name, Object value) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
            MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
            if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
                if (value == null && prop.getChildren() != null) {
                    // don't instantiate child path if value is null
                    return;
                } else {
                    metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
                }
            }
            metaValue.setValue(prop.getChildren(), value);
        } else {
            objectWrapper.set(prop, value);
        }
    }
    复制代码

      metaValue.setValue方法最后会调用到Java类中对应数据域的set方法,这样也就完成了SQL查询结果集的Java类封装过程。最后贴一张调用栈到达Java类的set方法中的快照:

  • 相关阅读:
    gffread: 从基因组提取fa
    linux: echo 保留变量的换行符
    R语言:按分隔符分割字符串并提取倒数三列
    linux:某列转为多行
    碎碎念六一
    抵达一颗安静的心
    zabbix 如何监控 tomcat中堆内存??
    JVM垃圾回收器
    Kubernetes 1.22.8高可用集群部署
    Redmine 5 安装(windows)
  • 原文地址:https://www.cnblogs.com/bootdo/p/10600167.html
Copyright © 2020-2023  润新知