Mybatis源碼解析-Mapper執行SQL過程

測試準備

添加mybatis-config.xml全局配置文件html

<?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>

    <!-- 引入外部資源配置文件 -->
    <properties resource="jdbc.properties"/>

    <settings>
        <!-- 開啓駝峯自動映射 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- 設置二級緩存開關 默認開啓 -->
        <setting name="cacheEnabled" value="true"/>
        <!-- 延遲加載的開關 默認關閉-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--
            true啓用時,當延遲加載開啓時訪問對象中一個懶對象屬性時,將徹底加載這個對象的全部懶對象屬性。
            false,當延遲加載時,按需加載對象屬性(即訪問對象中一個懶對象屬性,不會加載對象中其餘的懶對象屬性)。
            默認爲false
         -->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

    <typeAliases>
        <!-- 別名 type 指定java對象類型 alias 別名名稱 -->
         <typeAlias type="com.laravelshao.learning.mybatis.pojo.User" alias="User"/>
        <!-- 指定掃描包 Mybatis會將該包下全部類都生成別名(別名首字母不區分大小寫) -->
        <package name="com.laravelshao.learning.mybatis.pojo"/>
    </typeAliases>

    <!-- 配置分頁插件 -->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql"/>
            <!-- 使用RowBounds分頁是否進行count查詢 默認false-->
            <property name="rowBoundsWithCount" value="true"/>
        </plugin>
    </plugins>

    <!-- 配置環境 指定數據庫鏈接信息 -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!-- 引入mapper配置文件 -->
        <!--<mapper resource="com/laravelshao/learning/mybatis/mapper/UserMapper.xml"/>-->
        <!-- 配置mapper接口掃描包 -->
        <package name="com.laravelshao.learning.mybatis.mapper"/>
    </mappers>

</configuration>

jdbc.propertiesjava

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.username=root
jdbc.password=123456

添加實體類mysql

public class User implements Serializable {

    private Long id;

    // 用戶名
    private String userName;

    // 密碼
    private String password;

    // 姓名
    private String name;

    // 年齡
    private Integer age;

    // 性別 1 男性 2 女性
    private Integer sex;

    // 出生日期
    private Date birthday;

    // 建立時間
    private Date created;

    // 更新時間
    private Date updated;

    public Long getId() {
        return id;
    }

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

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getSex() {
        return sex;
    }

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

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Date getUpdated() {
        return updated;
    }

    public void setUpdated(Date updated) {
        this.updated = updated;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", userName=" + userName + ", password=" + password + ", name=" + name
                + ", age=" + age + ", sex=" + sex + ", birthday=" + birthday + ", created=" + created
                + ", updated=" + updated + "]";
    }
}

添加mapper接口laravel

public interface UserMapper {

    /**
     * 根據id查詢用戶信息
     *
     * @param id
     * @return
     */
    User queryUserById(Long id);

    /**
     * 查詢全部用戶數據
     *
     * @return
     */
    List<User> queryAll();

    /**
     * 新增用戶信息
     *
     * @param user
     */
    void saveUser(User user);

    /**
     * 更新用戶信息
     *
     * @param user
     */
    void updateUser(User user);

    /**
     * 根據id刪除用戶信息
     *
     * @param id
     */
    void deleteUserById(Long id);
}

添加mapper映射文件git

<?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">
<!-- namespace 命名空間(保持惟一) 如需mybatis生成mapper動態代理類 則必須使用mapper接口全路徑 -->
<mapper namespace="com.laravelshao.learning.mybatis.mapper.UserMapper">

    <!--
        id:ResultMap惟一標識
        type:結果集映射的java對象全路徑或者別名
        autoMapping:是否自動映射沒有映射關係的屬性和字段 默認開啓
    -->
    <resultMap id="userResultMap" type="User" autoMapping="true">
        <!-- column:字段名 property:屬性名 -->
        <id column="id" property="id"/>
        <result column="user_name" property="userName"/>
    </resultMap>

    <!-- 根據id查詢用戶信息 -->
    <!--<select id="queryUserById" resultType="User">-->
    <select id="queryUserById" resultMap="userResultMap">
        SELECT * FROM tb_user WHERE id = #{id}
    </select>

    <!-- 查詢全部用戶數據 -->
    <select id="queryAll" resultType="com.laravelshao.learning.mybatis.pojo.User">
      SELECT * FROM tb_user
   </select>

    <!-- 新增用戶信息 -->
    <insert id="saveUser" parameterType="com.laravelshao.learning.mybatis.pojo.User">
      INSERT INTO tb_user (
         id,
         user_name,
         password,
         name,
         age,
         sex,
         birthday,
         created,
         updated
      )
      VALUES
         (
            NULL,
            #{userName},
            #{password},
            #{name},
            #{age},
            #{sex},
            #{birthday},
            NOW(),
            NOW()
         );
   </insert>

    <!-- 更新用戶信息 -->
    <update id="updateUser" parameterType="com.laravelshao.learning.mybatis.pojo.User">
      UPDATE tb_user
      SET
        user_name = #{userName},
        password = #{password},
        name = #{name},
        age = #{age},
        sex = #{sex},
        birthday = #{birthday},
        updated = NOW()
      WHERE
        id = #{id}
   </update>

    <!-- 根據id刪除用戶信息 -->
    <delete id="deleteUserById" parameterType="java.lang.Long">
      DELETE FROM tb_user WHERE id = #{id}
   </delete>

</mapper>

添加測試方法github

public class UserMapperTest {

    private UserMapper userMapper;

    SqlSession sqlSession = null;
    SqlSessionFactory sqlSessionFactory = null;

    @Before
    public void setUp() throws Exception {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //openSession(boolean autoCommit) 設置事務是否自動提交
        sqlSession = sqlSessionFactory.openSession(true);
        // 獲取動態代理實現類
        this.userMapper = sqlSession.getMapper(UserMapper.class);
    }

    @Test
    public void testQueryUserById() {
        User user = userMapper.queryUserById(1L);
        System.out.println(user);
    }

    @Test
    public void testQueryAll() {
        List<User> users = userMapper.queryAll();
        for (User user : users) {
            System.out.println(user);
        }
    }

    @Test
    public void testSaveUser() {
        User user = new User();  
        user.setAge(20);
        user.setBirthday(new Date());
        user.setName("test_name_1");
        user.setPassword("123456");
        user.setSex(1);
        user.setUserName("test_userName_1");
        userMapper.saveUser(user);
    }

    @Test
    public void testUpdateUser() {
        User user = userMapper.queryUserById(2L);
        user.setAge(30);
        userMapper.updateUser(user);
    }

    @Test
    public void testDeleteUserById() {
        userMapper.deleteUserById(8L);
    }

}

執行過程分析

SqlSession建立

SqlSession建立經過調用DefaultSqlSessionFactory的openSession方法進而調用openSessionFromDataSource方法sql

// DefaultSqlSessionFactory
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;

    DefaultSqlSession var8;
    try {
        Environment environment = this.configuration.getEnvironment(); // 從全局配置類configuration獲取environment信息
        TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment); // 獲取事務工廠
        // 建立新的事務 
        // 參數1:數據源 參數2:事務隔離級別 參數3:是否自動提交事務
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        Executor executor = this.configuration.newExecutor(tx, execType); // 建立執行器
        var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
    } catch (Exception var12) {
        this.closeTransaction(tx);
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    } finally {
        ErrorContext.instance().reset();
    }

    return var8;
}

建立執行器數據庫

// Configuration
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? this.defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Object executor;
    // 根據執行器類型建立不一樣執行器
    if (ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
    } else {
        executor = new SimpleExecutor(this, transaction);
    }

    if (this.cacheEnabled) {
        executor = new CachingExecutor((Executor)executor);
    }

    Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
    return executor;
}

摘自mybatis官網緩存

ExecutorType枚舉類型定義了三個值:session

• ExecutorType.SIMPLE:這個執行器類型不作特殊的事情。它爲每一個語句的執行建立一個新的預處理語句。

• ExecutorType.REUSE:這個執行器類型會複用預處理語句。

• ExecutorType.BATCH:這個執行器會批量執行全部更新語句,若是SELECT在它們中間執行,必要時請把它們區分開來以保證行爲的易讀性。

Executor定義了具體執行SQL方法

public interface Executor {
    ResultHandler NO_RESULT_HANDLER = null;

    int update(MappedStatement var1, Object var2) throws SQLException;

    <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;

    <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;

    <E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;

    List<BatchResult> flushStatements() throws SQLException;

    void commit(boolean var1) throws SQLException;

    void rollback(boolean var1) throws SQLException;

    CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);

    boolean isCached(MappedStatement var1, CacheKey var2);

    void clearLocalCache();

    void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class<?> var5);

    Transaction getTransaction();

    void close(boolean var1);

    boolean isClosed();

    void setExecutorWrapper(Executor var1);
}

Mapper代理類建立

調用DefaultSqlSession的getMapper方法,進而調用MapperRegistry,最後調用MapperProxyFactory最終建立出mapper代理對象

// DefaultSqlSession
public <T> T getMapper(Class<T> type) {
    return this.configuration.getMapper(type, this);
}
// Configuration
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return this.mapperRegistry.getMapper(type, sqlSession);
}
// MapperRegistry
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    } else {
        try {
            return mapperProxyFactory.newInstance(sqlSession); // 交給mapper代理工廠建立對象
        } catch (Exception var5) {
            throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
        }
    }
}

Mapper代理工廠建立代理類對象

// MapperProxyFactory
public T newInstance(SqlSession sqlSession) {
    MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
    return this.newInstance(mapperProxy);
}

protected T newInstance(MapperProxy<T> mapperProxy) {
    // JDK動態代理生成mapper代理類對象
    return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}

SQL執行流程

查詢流程分析

這裏以方法queryAll爲例分析SQL執行過程

@Test
public void testQueryAll() {
    List<User> users = userMapper.queryAll();
    for (User user : users) {
        System.out.println(user);
    }
}

調用代理類的invoke方法

// MapperProxy
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }

        if (this.isDefaultMethod(method)) {
            return this.invokeDefaultMethod(proxy, method, args);
        }
    } catch (Throwable var5) {
        throw ExceptionUtil.unwrapThrowable(var5);
    }

    MapperMethod mapperMethod = this.cachedMapperMethod(method);
    return mapperMethod.execute(this.sqlSession, args); // 執行mapper方法
}

執行具體方法

// MapperMethod
public Object execute(SqlSession sqlSession, Object[] args) {
    Object param;
    Object result;
    switch(this.command.getType()) { // 根據方法類型執行不一樣方法
    case INSERT:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
        break;
    case UPDATE:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
        break;
    case DELETE:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
        break;
    case SELECT:
        if (this.method.returnsVoid() && this.method.hasResultHandler()) {
            this.executeWithResultHandler(sqlSession, args);
            result = null;
        } else if (this.method.returnsMany()) {
            result = this.executeForMany(sqlSession, args);
        } else if (this.method.returnsMap()) {
            result = this.executeForMap(sqlSession, args);
        } else if (this.method.returnsCursor()) {
            result = this.executeForCursor(sqlSession, args);
        } else {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = sqlSession.selectOne(this.command.getName(), param);
        }
        break;
    case FLUSH:
        result = sqlSession.flushStatements();
        break;
    default:
        throw new BindingException("Unknown execution method for: " + this.command.getName());
    }

    if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
        throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
    } else {
        return result;
    }
}

這裏,咱們查詢全部用戶,執行executeForMany方法

// MapperMethod
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    Object param = this.method.convertArgsToSqlCommandParam(args);
    List result;
    if (this.method.hasRowBounds()) {
        RowBounds rowBounds = this.method.extractRowBounds(args);
        result = sqlSession.selectList(this.command.getName(), param, rowBounds);
    } else {
        result = sqlSession.selectList(this.command.getName(), param);
    }

    if (!this.method.getReturnType().isAssignableFrom(result.getClass())) {
        return this.method.getReturnType().isArray() ? this.convertToArray(result) : this.convertToDeclaredCollection(sqlSession.getConfiguration(), result);
    } else {
        return result;
    }
}

調用默認實現DefaultSqlSession的selectList方法

// DefaultSqlSession
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) {
    List var5;
    try {
        MappedStatement ms = this.configuration.getMappedStatement(statement); // 根據statement ID從configuration配置中獲取MappedStatement
        // 執行器執行查詢任務
        var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception var9) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
    } finally {
        ErrorContext.instance().reset();
    }

    return var5;
}
// BaseExecutor
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter); // 根據參數動態組裝執行sql
    CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql); // 爲當前查詢任務建立緩存key
    return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

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 (this.closed) {
        throw new ExecutorException("Executor was closed.");
    } else {
        if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
            this.clearLocalCache();
        }

        List list;
        try {
            ++this.queryStack;
            list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
            if (list != null) {
                this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            } else {
                list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); // 從數據庫中查詢
            }
        } finally {
            --this.queryStack;
        }

        if (this.queryStack == 0) {
            Iterator var8 = this.deferredLoads.iterator();

            while(var8.hasNext()) {
                BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
                deferredLoad.load();
            }

            this.deferredLoads.clear();
            if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                this.clearLocalCache();
            }
        }

        return list;
    }
}

// 數據庫查詢
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

    List list;
    try {
        list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);  // 查詢結果
    } finally {
        this.localCache.removeObject(key); 
    }

    this.localCache.putObject(key, list); // 將結果放入緩存中
    if (ms.getStatementType() == StatementType.CALLABLE) {
        this.localOutputParameterCache.putObject(key, parameter);
    }

    return list;
}

調用SimpleExecutor的doQuery方法

// SimpleExecutor
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;

    List var9;
    try {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        stmt = this.prepareStatement(handler, ms.getStatementLog()); // 準備語句
        var9 = handler.query(stmt, resultHandler);
    } finally {
        this.closeStatement(stmt);
    }

    return var9;
}

準備語句

// SimpleExecutor
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Connection connection = this.getConnection(statementLog); // 獲取一個數據庫鏈接對象
    Statement stmt = handler.prepare(connection, this.transaction.getTimeout()); // 使用StatementHandler從數據庫鏈接對象中獲取Statement對象
    handler.parameterize(stmt); // 設置參數 將sql語句中的佔位符替換爲具體參數
    return stmt;
}

調用PreparedStatementHandler最終執行sql語句

// PreparedStatementHandler
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement)statement;
    ps.execute(); // 執行具體sql語句
    return this.resultSetHandler.handleResultSets(ps); // 處理結果集
}

從上面能夠看出Executor的執行過程

  • 首先獲取一個數據庫鏈接對象
  • 使用StatementHandler.prepare()方法從數據庫鏈接對象中獲取Statement對象
  • 使用StatementHandler.parameterize()方法將sql中佔位符替換爲具體執行參數
  • 最後調用StatementHandler. query()方法執行sql

處理結果集

// DefaultResultSetHandler
public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
    List<Object> multipleResults = new ArrayList();
    int resultSetCount = 0;
    ResultSetWrapper rsw = this.getFirstResultSet(stmt);
    List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    this.validateResultMapsCount(rsw, resultMapCount);

    while(rsw != null && resultMapCount > resultSetCount) {
        ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
        this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
        rsw = this.getNextResultSet(stmt);
        this.cleanUpAfterHandlingResultSet();
        ++resultSetCount;
    }

    String[] resultSets = this.mappedStatement.getResultSets();
    if (resultSets != null) {
        while(rsw != null && resultSetCount < resultSets.length) {
            ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
            if (parentMapping != null) {
                String nestedResultMapId = parentMapping.getNestedResultMapId();
                ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
                this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
            }

            rsw = this.getNextResultSet(stmt);
            this.cleanUpAfterHandlingResultSet();
            ++resultSetCount;
        }
    }

    return this.collapseSingleResultList(multipleResults);
}

整個執行過程完成。

更新流程分析

下面再看看更新操做,以saveUser方法爲例分析更新流程(最終調用的都是update方法)

@Test
public void testSaveUser() {
    User user = new User();
    user.setAge(20);
    user.setBirthday(new Date());
    user.setName("test_name_22");
    user.setPassword("123456");
    user.setSex(1);
    user.setUserName("test_userName_22");
    userMapper.saveUser(user);
}

執行insert、update、delete最後調用的都是下面update方法

// DefaultSqlSession
public int update(String statement, Object parameter) {
    int var4;
    try {
        this.dirty = true;
        MappedStatement ms = this.configuration.getMappedStatement(statement); // 獲取對應的MappedStatement
        var4 = this.executor.update(ms, this.wrapCollection(parameter)); // 執行更新操做(包括包裝集合類型參數)
    } catch (Exception var8) {
        throw ExceptionFactory.wrapException("Error updating database.  Cause: " + var8, var8);
    } finally {
        ErrorContext.instance().reset();
    }

    return var4;
}
// BaseExecutor
public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
    if (this.closed) {
        throw new ExecutorException("Executor was closed.");
    } else {
        this.clearLocalCache(); // 清除本地緩存
        return this.doUpdate(ms, parameter); // 執行更新操做
    }
}
// SimpleExecutor
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;

    int var6;
    try {
        Configuration configuration = ms.getConfiguration(); // 獲取全局配置信息configuration
        // 建立StatementHandler
        StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
        stmt = this.prepareStatement(handler, ms.getStatementLog()); // 準備Statement
        var6 = handler.update(stmt); // 處理更新操做
    } finally {
        this.closeStatement(stmt);
    }

    return var6;
}

最終執行update語句

// PreparedStatementHandler
public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement)statement;
    ps.execute(); // 執行語句
    int rows = ps.getUpdateCount(); // 獲取更新行數
    Object parameterObject = this.boundSql.getParameterObject();
    KeyGenerator keyGenerator = this.mappedStatement.getKeyGenerator(); // KeyGenerator後置處理
    keyGenerator.processAfter(this.executor, this.mappedStatement, ps, parameterObject);
    return rows;
}

這裏重點分析下使用selectKey標籤的SelectKeyGenerator實現

// SelectKeyGenerator
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    if (!this.executeBefore) { // 若是不是前置處理
        this.processGeneratedKeys(executor, ms, parameter); // 處理生成的key
    }

}

private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
    try {
        if (parameter != null && this.keyStatement != null && this.keyStatement.getKeyProperties() != null) {
            String[] keyProperties = this.keyStatement.getKeyProperties(); // 獲取配置key的屬性信息
            Configuration configuration = ms.getConfiguration(); // 獲取全局配置參數configuration參數
            MetaObject metaParam = configuration.newMetaObject(parameter);
            if (keyProperties != null) {
                Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE); // 建立執行器
                List<Object> values = keyExecutor.query(this.keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER); // 執行查詢操做
                if (values.size() == 0) { // 返回結果校驗
                    throw new ExecutorException("SelectKey returned no data.");
                }

                if (values.size() > 1) {
                    throw new ExecutorException("SelectKey returned more than one value.");
                }

                MetaObject metaResult = configuration.newMetaObject(values.get(0));
                if (keyProperties.length == 1) { // 只有一個參數屬性
                    if (metaResult.hasGetter(keyProperties[0])) {
                        this.setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
                    } else {
                        this.setValue(metaParam, keyProperties[0], values.get(0));
                    }
                } else {
                    this.handleMultipleProperties(keyProperties, metaParam, metaResult); // 多個參數屬性
                }
            }
        }

    } catch (ExecutorException var10) {
        throw var10;
    } catch (Exception var11) {
        throw new ExecutorException("Error selecting key or setting result to parameter object. Cause: " + var11, var11);
    }
}

private void handleMultipleProperties(String[] keyProperties, MetaObject metaParam, MetaObject metaResult) {
    String[] keyColumns = this.keyStatement.getKeyColumns();
    if (keyColumns != null && keyColumns.length != 0) { 
        if (keyColumns.length != keyProperties.length) {
            throw new ExecutorException("If SelectKey has key columns, the number must match the number of key properties.");
        }

        for(int i = 0; i < keyProperties.length; ++i) {
            this.setValue(metaParam, keyProperties[i], metaResult.getValue(keyColumns[i])); // 若是沒有指定列表 則直接使用屬性名稱賦值
        }
    } else {
        String[] var5 = keyProperties;
        int var6 = keyProperties.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            String keyProperty = var5[var7];
            this.setValue(metaParam, keyProperty, metaResult.getValue(keyProperty)); // 使用列名賦值
        }
    }

}

根據方法返回值類型將影響行數轉換爲指定類型

// MapperMethod
private Object rowCountResult(int rowCount) {
    Object result;
    if (this.method.returnsVoid()) {
        result = null;
    } else if (!Integer.class.equals(this.method.getReturnType()) && !Integer.TYPE.equals(this.method.getReturnType())) {
        if (!Long.class.equals(this.method.getReturnType()) && !Long.TYPE.equals(this.method.getReturnType())) {
            if (!Boolean.class.equals(this.method.getReturnType()) && !Boolean.TYPE.equals(this.method.getReturnType())) {
                throw new BindingException("Mapper method '" + this.command.getName() + "' has an unsupported return type: " + this.method.getReturnType());
            }

            result = rowCount > 0;
        } else {
            result = (long)rowCount;
        }
    } else {
        result = rowCount;
    }

    return result;
}

更新操做流程分析完成。

參考資料

https://blog.csdn.net/ashan_li/article/details/50378393

https://blog.csdn.net/luanlouis/article/details/40422941

https://blog.csdn.net/heroqiang/article/details/79121516

https://www.cnblogs.com/jeffen/p/6277696.html?utm_source=itdadao&utm_medium=referral

http://www.ccblog.cn/88.htm

相關文章
相關標籤/搜索