MyBatis源碼分析-SQL語句執行的完整流程

  MyBatis 是支持定製化 SQL、存儲過程以及高級映射的優秀的持久層框架。MyBatis 避免了幾乎全部的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 能夠對配置和原生Map使用簡單的 XML 或註解,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄。如何新建MyBatis源碼工程請點擊MyBatis源碼分析-IDEA新建MyBatis源碼工程html

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

  1. 根據JDBC規範創建與數據庫的鏈接。
  2. 經過反射打通Java對象與數據庫參數交互之間相互轉換的關係。

  MyBatis框架是一種典型的交互式框架,先準備好交互的必要條件,而後構建一個交互的環境,在交互環境中劃分會話,在會話中與數據庫進行交互數據。mysql

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類。git

(圖片來自《深刻理解mybatis原理》 MyBatis的架構設計以及實例分析)github

2 SQL執行流程

  MyBatis主要設計目的仍是爲了讓咱們在執行SQL時對輸入輸出的數據的管理更加方便,因此方便的讓咱們寫出SQL和方便的獲取SQL的執行結果是MyBatis的核心競爭力。下面就用一個例子來從源碼角度看一下SQL的完整執行流程。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>
conf.xml

首先創建數據表,這裏就以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 - 用戶類
 */
public class User {

    public static final int MAN  = 0;   // 男生
    public static final int WOMAN = 1;  // 女生
    public static final int OTHER = 2;  // 其餘

    private int id;             // 用戶id
    private String name;        // 用戶名
    private String password;    // 用戶密碼
    private int sex;            // 用戶性別
    private String email;       // 用戶郵箱
    private String phone;       // 用戶手機
    private String admin;       // 用戶是不是管理員,"admin"表示是管理員,其餘爲普通用戶

    public User() { }

    public User(String name, String password, int sex, String email, String phone) {
        this.name = name;
        this.password = password;
        this.sex = sex;
        this.email = email;
        this.phone = phone;
        this.admin = "";
    }
    public User(String name, String password, String sex, String email, String phone) {
        this.name = name;
        this.password = password;
        setSex(sex); // this.sex = sex;
        this.email = email;
        this.phone = phone;
        this.admin = "";
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    public void setSex(String sexStr) {
        int sex = Integer.valueOf(sexStr);
        switch (Integer.valueOf(sexStr)) {
            case 0: {
                this.sex = MAN;
                break;
            }
            case 1: {
                this.sex = WOMAN;
                break;
            }
            default: {
                this.sex = OTHER;
                break;
            }
        }
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getAdmin() {
        return admin;
    }

    public void setAdmin(String admin) {
        this.admin = admin;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", sex=" + sex +
                ", email='" + email + '\'' +
                ", phone='" + phone + '\'' +
                ", admin='" + admin + '\'' +
                '}';
    }

}
User

再新建usre表的配置文件:session

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.luoxn28.dao.UserDao">

    <select id="getById" parameterType="int" resultType="User">
        SELECT * FROM user WHERE id=#{id}; <!-- #{xxx} xxx爲類中的數據域名稱 -->
    </select>

    <select id="getAll" resultType="com.luoxn28.dao.User">
        SELECT * FROM user;
    </select>
</mapper>
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方法中的快照:

 

參考:

  一、MyBatis源碼

  二、《深刻分析Java Web技術內幕》的iBatis章節

  三、《深刻理解mybatis原理》 MyBatis的架構設計以及實例分析

  四、luoxn28/tuiku

  五、Java JDBC基礎學習小結

相關文章
相關標籤/搜索