MyBatis-Spring 執行SQL語句的流程


1. 從SqlSessionDaoSupport開始

通常我們使用MyBatis會讓自己的DAO繼承SqlSessionDaoSupport,那么SqlSessionDaoSupport是如何運作的呢,下面是SqlSessionDaoSupport的源代碼

/*
 *    Copyright 2010 The myBatis Team
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.mybatis.spring.support;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.support.DaoSupport;
import org.springframework.util.Assert;

/**
 * Convenient super class for MyBatis SqlSession data access objects.
 * It gives you access to the template which can then be used to execute SQL methods.
 * <p>
 * This class needs a SqlSessionTemplate or a SqlSessionFactory.
 * If both are set the SqlSessionFactory will be ignored.
 *
 * @see #setSqlSessionFactory
 * @see #setSqlSessionTemplate
 * @see SqlSessionTemplate
 * @version $Id: SqlSessionDaoSupport.java 3266 2010-11-22 06:56:51Z simone.tripodi $
 */
public abstract class SqlSessionDaoSupport extends DaoSupport {

    // 這個SqlSession就是我們平時用來執行SQL和事務的一次會話
    private SqlSession sqlSession;

    private boolean externalSqlSession;

    // 可以看到以下兩個Autowired的set方法,實際上他們的功能都是設置sqlSession的實例
    // 區別在於一個是通過傳入sqlSessionFactory然后包裝成SqlSessionTemplate
    // 另一個直接傳入SqlSessionTemplate賦值給sqlSession
    @Autowired(required = false)
    public final void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        if (!this.externalSqlSession) {
            this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
        }
    }

    @Autowired(required = false)
    public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSession = sqlSessionTemplate;
        this.externalSqlSession = true;
    }

    /**
     * Users should use this method to get a SqlSession to call its statement methods
     * This is SqlSession is managed by spring. Users should not commit/rollback/close it
     * because it will be automatically done.
     *
     * @return Spring managed thread safe SqlSession 
     */
    public final SqlSession getSqlSession() {
        return this.sqlSession;
    }

    /**
     * {@inheritDoc}
     */
    protected void checkDaoConfig() {
        Assert.notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
    }

}

 

2.SqlSessionDaoSupport中的SqlSession產生

先提一提既然我們是用dao去繼承這個SqlSessionDaoSupport,觀察我們的dao的配置

    <bean id="userDao" class="dao.UserDao">
        <!--<property name="sqlSessionFactory" ref="sqlSessionFactory" />-->
        <property name="sqlSessionTemplate" ref="sqlSession" />
    </bean>

    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" >
        <!-- 第一個參數是 sqlSessionFactory -->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
        <!-- 第二個參數是 ExecutorType -->
        <constructor-arg index="1" ref="BATCH"/>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 指定數據源 -->
        <property name="dataSource" ref="dataSource" />
        <!-- 指定MyBatis配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml" />
        <!-- 導入Mapper -->
        <property name="mapperLocations" value="classpath*:mappers/*.xml" />
    </bean>

    <!-- datasource -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatistest?characterEncoding=utf8" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>

觀察我們的dao的配置可以發現我們可以配置sqlSessionFactory和sqlSessionTemplate,實際上我們配置sqlSessionTemplate的話就是能多配置一個ExecutorType參數(這個參數在MyBatis的Settings參數里也可以找到,叫做defaultExecutorType,參數配置詳細介紹見 http://mybatis.github.io/mybatis-3/configuration.html),這個參數的選項有三個:

SIMPLE: 普通SQL執行器,不會使用預解析和批量處理

REUSE: 重用prepared statement

BATCH: 不但重用prepared statement,而且能執行批量處理,如

public void insertUsers(User[] users) {
   for (User user : users) {
     sqlSession.insert("org.mybatis.spring.sample.mapper.UserMapper.insertUser", user);
   }
 }

 

3. 分析sqlSessionTemplate的構造

查看sqlSessionTemplate的源代碼,關鍵部分如下

    /**
     * Constructs a Spring managed {@link SqlSession} with the given
     * {@link SqlSessionFactory} and {@link ExecutorType}.
     * A custom {@link SQLExceptionTranslator} can be provided as an 
     * argument so any {@link PersistenceException} thrown by MyBatis
     * can be custom translated to a {@link RuntimeException}
     * The {@link SQLExceptionTranslator} can also be null and thus no
     * exception translation will be done and MyBatis exceptions will be 
     * thrown
     *
     * @param sqlSessionFactory
     * @param executorType
     * @param exceptionTranslator
     */
    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
            PersistenceExceptionTranslator exceptionTranslator) {

        Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
        Assert.notNull(executorType, "Property 'executorType' is required");

        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;
        this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
                SqlSessionFactory.class.getClassLoader(),
                new Class[] { SqlSession.class }, 
                new SqlSessionInterceptor());
    }

它的功能如下

1) 設置sqlSessionFactory,查看上面的xml的配置,就知道sqlSessionFactory這個東西其實就是用來指定datasource,讀取mybatis配置(environment,settings種種),還有讀取mybatis的各個sql語句的mapper的東西

2) 設置executor type,上面已經介紹過了

3) 設置exception translator,這個東西是用來將jdbc的異常轉換成spring的sql異常的東西(因為jdbc的異常很單一,無法詳細的表達出錯時錯誤到底是什么,所以spring自己寫了一套更好理解的異常,這是題外話),這個東西保持默認就可以了,所以我們在配置sqlSessionTemplate的bean的時候並沒有配置這個參數,如果沒有配置,則構造方法會使用一個默認的(這是一個重載方法,另外還有一個構造方法是不需要設置這個參數的,那個構造方法調用了這個構造方法,其中execeptionTranslator就是傳了一個默認的進來)

4) 對SqlSessionInteceptor()設置了一個代理,從這個動態代理的構造函數參數我們就能看出來這個東西是一個SqlSession

其中,這個SqlSessionInterceptor是SqlSessionTemplate一個內部類,他返回了一個SqlSession關鍵代碼如下

            final SqlSession sqlSession = SqlSessionUtils.getSqlSession(
                    SqlSessionTemplate.this.sqlSessionFactory,
                    SqlSessionTemplate.this.executorType);

很簡單,其實就是用sqlSessionFactory和executorType生成了一個sqlSession

接下來進入一段七彎八拐的調用,過程如下

SqlSessionUtils.getSqlSession() -> SessionFactory.openSession(executorTypeconn) -> DefaultSessionFactory.openSession(ExecutorType execType) ->Configuration.newExecutor(txexecTypeautoCommit) -> return new DefaultSqlSession(configurationexecutor)

其中的關鍵部分是newExecutor(),這玩意就是生成SQL語句執行器的地方,生成的代碼如下:
  public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    // 這里就是根據ExecutorType創建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);
    }
    // 這句就是判斷setting里的cacheEnabled參數的地方
    // 所以設置了cacheEnabled參數后就會被包裝成緩存Executor
    if (cacheEnabled) {
      executor = new CachingExecutor(executor, autoCommit);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
 
        

 

主要做的事情就是 1) 根據executorType生成合適的executor 2) 更具cacheEnabled參數包裝executor 



至此, SqlSessionTemplate中的sqlSessionProxy的executor終於生成出來,以后我們使用dao中的session來執行sql相關的操作用的就都是這個SqlSessionTemplate中的sqlSessionProxy

最后,畫個圖總結一下


也就是說,我們其實使用的是SqlSessionTemplate在做各種數據庫操作,這個東西讀取了我們的datasource和mybatisconfig,用它的Executor去執行我們Mapper里的sql語句來獲取查詢結果


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM