欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MyBatis?SqlSession執(zhí)行流程小結(jié)

 更新時(shí)間:2025年05月22日 09:57:54   作者:一名技術(shù)極客  
本文主要介紹了MyBatis?SqlSession執(zhí)行流程小結(jié),涵蓋動(dòng)態(tài)代理、Executor、StatementHandler等組件,及參數(shù)處理、結(jié)果映射與緩存機(jī)制,感興趣的可以了解一下

SqlSession

正如其名,Sqlsession對(duì)應(yīng)著一次數(shù)據(jù)庫(kù)會(huì)話。由于數(shù)據(jù)庫(kù)會(huì)話不是永久的,因此Sqlsession的生命周期也不應(yīng)該是永久的,相反,在你每次訪問(wèn)數(shù)據(jù)庫(kù)時(shí)都需要?jiǎng)?chuàng)建它(當(dāng)然并不是說(shuō)在Sqlsession里只能執(zhí)行一次sql,你可以執(zhí)行多次,當(dāng)一旦關(guān)閉了Sqlsession就需要重新創(chuàng)建它)。

那么咱們就先看看是怎么獲取SqlSession:

在這里插入圖片描述

  • 首先,SqlSessionFactoryBuilder去讀取mybatis的配置文件,然后build一個(gè)DefaultSqlSessionFactory。 源碼如下:
/**
 * 一系列的構(gòu)造方法最終都會(huì)調(diào)用本方法(配置文件為Reader時(shí)會(huì)調(diào)用本方法,還有一個(gè)InputStream方法與此對(duì)應(yīng))
 * @param reader
 * @param environment
 * @param properties
 * @return
 */
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
        //通過(guò)XMLConfigBuilder解析配置文件,解析的配置相關(guān)信息都會(huì)封裝為一個(gè)Configuration對(duì)象
        XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
        //這兒創(chuàng)建DefaultSessionFactory對(duì)象
        return build(parser.parse());
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
        ErrorContext.instance().reset();
        try {
            reader.close();
        } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
        }
    }
}

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}
  • 當(dāng)我們獲取到SqlSessionFactory之后,就可以通過(guò)SqlSessionFactory去獲取SqlSession對(duì)象。 源碼如下:
/**
 * 通常一系列openSession方法最終都會(huì)調(diào)用本方法
 * @param execType
 * @param level
 * @param autoCommit
 * @return
 */
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        //通過(guò)Confuguration對(duì)象去獲取Mybatis相關(guān)配置信息, Environment對(duì)象包含了數(shù)據(jù)源和事務(wù)的配置
        final Environment environment = configuration.getEnvironment();
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        //之前說(shuō)了,從表面上來(lái)看,咱們是用sqlSession在執(zhí)行sql語(yǔ)句, 實(shí)際呢,其實(shí)是通過(guò)excutor執(zhí)行, excutor是對(duì)于Statement的封裝
        final Executor executor = configuration.newExecutor(tx, execType);
        //關(guān)鍵看這兒,創(chuàng)建了一個(gè)DefaultSqlSession對(duì)象
        return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
        closeTransaction(tx); // may have fetched a connection so lets call close()
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}
  • 通過(guò)以上步驟,咱們已經(jīng)得到SqlSession對(duì)象了。接下來(lái)就是該干嘛干嘛去了(話說(shuō)還能干嘛,當(dāng)然是執(zhí)行sql語(yǔ)句咯)
SqlSessionFactory sessionFactory = null;  
String resource = "mybatis-conf.xml";  
try {
     //SqlSessionFactoryBuilder讀取配置文件
     sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader(resource));
} catch (IOException e) {  
     e.printStackTrace();  
}    
//通過(guò)SqlSessionFactory獲取SqlSession
SqlSession sqlSession = sessionFactory.openSession();
  • 創(chuàng)建Sqlsession的地方只有一個(gè),那就是SqlsessionFactory的openSession方法:
public SqlSessionopenSession() {  
    return openSessionFromDataSource(configuration.getDefaultExecutorType(),null, false);  
}

我們可以看到實(shí)際創(chuàng)建SqlSession的地方是openSessionFromDataSource,如下:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
      Connection connection = null;
      try {
          final Environment environment = configuration.getEnvironment();
          final DataSource dataSource = getDataSourceFromEnvironment(environment);
          // MyBatis對(duì)事務(wù)的處理相對(duì)簡(jiǎn)單,TransactionIsolationLevel中定義了幾種隔離級(jí)別,并不支持內(nèi)嵌事務(wù)這樣較復(fù)雜的場(chǎng)景,同時(shí)由于其是持久層的緣故,所以真正在應(yīng)用開(kāi)發(fā)中會(huì)委托Spring來(lái)處理事務(wù)實(shí)現(xiàn)真正的與開(kāi)發(fā)者隔離。分析事務(wù)的實(shí)現(xiàn)是個(gè)入口,借此可以了解不少JDBC規(guī)范方面的事情。
          TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          connection = dataSource.getConnection();
          if (level != null) {  
              connection.setTransactionIsolation(level.getLevel());
          }
          connection = wrapConnection(connection);
          Transaction tx = transactionFactory.newTransaction(connection,autoCommit);
          Executor executor = configuration.newExecutor(tx, execType);
          return newDefaultSqlSession(configuration, executor, autoCommit);
      } catch (Exceptione) {  
          closeConnection(connection);  
          throwExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);  
      } finally {
          ErrorContext.instance().reset();
      }
}

可以看出,創(chuàng)建sqlsession經(jīng)過(guò)了以下幾個(gè)主要步驟:

  • 從配置中獲取Environment;
  • 從Environment中取得DataSource;
  • 從Environment中取得TransactionFactory;
  • 從DataSource里獲取數(shù)據(jù)庫(kù)連接對(duì)象Connection;
  • 在取得的數(shù)據(jù)庫(kù)連接上創(chuàng)建事務(wù)對(duì)象Transaction;
  • 創(chuàng)建Executor對(duì)象(該對(duì)象非常重要,事實(shí)上sqlsession的所有操作都是通過(guò)它完成的);
  • 創(chuàng)建sqlsession對(duì)象。

SqlSession咱們也拿到了,咱們可以調(diào)用SqlSession中一系列的select..., insert..., update..., delete...方法輕松自如的進(jìn)行CRUD操作了。就這樣?那咱配置的映射文件去哪兒了?別急,咱們接著往下看。

MapperProxy

在這里插入圖片描述

在mybatis中,通過(guò)MapperProxy動(dòng)態(tài)代理咱們的dao,也就是說(shuō),當(dāng)咱們執(zhí)行自己寫(xiě)的dao里面的方法的時(shí)候,其實(shí)是對(duì)應(yīng)的mapperProxy在代理。那么,咱們就看看怎么獲取MapperProxy對(duì)象吧:

  • 通過(guò)SqlSession從Configuration中獲取。 源碼如下:
/**
 * 什么都不做,直接去configuration中找, 哥就是這么任性
 */
@Override
public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
}
  • SqlSession把包袱甩給了Configuration, 接下來(lái)就看看Configuration。 源碼如下:
/**
 * 燙手的山芋,俺不要,你找mapperRegistry去要
 * @param type
 * @param sqlSession
 * @return
 */
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}
  • Configuration不要這燙手的山芋,接著甩給了MapperRegistry, 那咱看看MapperRegistry。 源碼如下:
/**
 * 爛活凈讓我來(lái)做了,沒(méi)法了,下面沒(méi)人了,我不做誰(shuí)來(lái)做
 * @param type
 * @param sqlSession
 * @return
 */
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 能偷懶的就偷懶,俺把粗活交給MapperProxyFactory去做
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
        //關(guān)鍵在這兒
        return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}
  • MapperProxyFactory是個(gè)苦B的人,粗活最終交給它去做了。 咱們看看源碼:
/**
 * 別人虐我千百遍,我待別人如初戀
 * @param mapperProxy
 * @return
 */
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
    //動(dòng)態(tài)代理我們寫(xiě)的dao接口
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

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

通過(guò)以上的動(dòng)態(tài)代理,咱們就可以方便地使用dao接口啦。

UserDao userMapper = sqlSession.getMapper(UserDao.class);  
User insertUser = new User();

這下方便多了吧,呵呵, 貌似mybatis的源碼就這么一回事兒啊。具體詳細(xì)介紹,請(qǐng)參見(jiàn)MyBatis Mapper 接口如何通過(guò)JDK動(dòng)態(tài)代理來(lái)包裝SqlSession 源碼分析。別急,還沒(méi)完, 咱們還沒(méi)看具體是怎么執(zhí)行sql語(yǔ)句的呢。

Excutor

Executor與Sqlsession的關(guān)系就像市長(zhǎng)與書(shū)記,Sqlsession只是個(gè)門(mén)面,真正干事的是Executor,Sqlsession對(duì)數(shù)據(jù)庫(kù)的操作都是通過(guò)Executor來(lái)完成的。與Sqlsession一樣,Executor也是動(dòng)態(tài)創(chuàng)建的:

在這里插入圖片描述


Executor創(chuàng)建的源代碼:

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ?ExecutorType.SIMPLE : executorType;
    Executor executor;
    if(ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this,transaction);
    } else if(ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this,transaction);  
    } else {  
        executor = newSimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
        executor = new CachingExecutor(executor);  
    }
    executor = (Executor) interceptorChain.pluginAll(executor);  
    return executor;
}  
  • 可以看出,如果不開(kāi)啟cache的話,創(chuàng)建的Executor只是3中基礎(chǔ)類(lèi)型之一,BatchExecutor專(zhuān)門(mén)用于執(zhí)行批量sql操作,ReuseExecutor會(huì)重用statement執(zhí)行sql操作,SimpleExecutor只是簡(jiǎn)單執(zhí)行sql沒(méi)有什么特別的。

  • 開(kāi)啟cache的話(默認(rèn)是開(kāi)啟的并且沒(méi)有任何理由去關(guān)閉它),就會(huì)創(chuàng)建CachingExecutor,它以前面創(chuàng)建的Executor作為唯一參數(shù)。CachingExecutor在查詢數(shù)據(jù)庫(kù)前先查找緩存,若沒(méi)找到的話調(diào)用delegate(就是構(gòu)造時(shí)傳入的Executor對(duì)象)從數(shù)據(jù)庫(kù)查詢,并將查詢結(jié)果存入緩存中。

Executor對(duì)象是可以被插件攔截的,如果定義了針對(duì)Executor類(lèi)型的插件,最終生成的Executor對(duì)象是被各個(gè)插件插入后的代理對(duì)象。

接下來(lái),咱們才要真正去看sql的執(zhí)行過(guò)程了。上面,咱們拿到了MapperProxy, 每個(gè)MapperProxy對(duì)應(yīng)一個(gè)dao接口, 那么咱們?cè)谑褂玫臅r(shí)候,MapperProxy是怎么做的呢? 源碼奉上:

  • MapperProxy: 我們知道對(duì)被代理對(duì)象的方法的訪問(wèn)都會(huì)落實(shí)到代理者的invoke上來(lái),MapperProxy的invoke如下:
/**
 * MapperProxy在執(zhí)行時(shí)會(huì)觸發(fā)此方法
 */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
        try {
            return method.invoke(this, args);
       } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
       }
    }
   final MapperMethod mapperMethod = cachedMapperMethod(method);
   //二話不說(shuō),主要交給MapperMethod自己去管
   return mapperMethod.execute(sqlSession, args);
}
  • MapperMethod: 就像是一個(gè)分發(fā)者,他根據(jù)參數(shù)和返回值類(lèi)型選擇不同的sqlsession方法來(lái)執(zhí)行。這樣mapper對(duì)象與sqlsession就真正的關(guān)聯(lián)起來(lái)了。
/**
 * 看著代碼不少,不過(guò)其實(shí)就是先判斷CRUD類(lèi)型,然后根據(jù)類(lèi)型去選擇到底執(zhí)行sqlSession中的哪個(gè)方法,繞了一圈,又轉(zhuǎn)回sqlSession了
 * @param sqlSession
 * @param args
 * @return
 */
public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    if (SqlCommandType.INSERT == command.getType()) {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
    } else if (SqlCommandType.UPDATE == command.getType()) {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
    } else if (SqlCommandType.SELECT == command.getType()) {
        if (method.returnsVoid() && method.hasResultHandler()) {
            executeWithResultHandler(sqlSession, args);
            result = null;
        } else if (method.returnsMany()) {
            result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
            result = executeForMap(sqlSession, args);
        } else {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = sqlSession.selectOne(command.getName(), param);
        }
    } else {
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
        throw new BindingException("Mapper method '" + command.getName()
        + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
}

既然又回到SqlSession了,前面提到過(guò),sqlsession只是一個(gè)門(mén)面,真正發(fā)揮作用的是executor,對(duì)sqlsession方法的訪問(wèn)最終都會(huì)落到executor的相應(yīng)方法上去。Executor分成兩大類(lèi),一類(lèi)是CacheExecutor,另一類(lèi)是普通Executor。Executor的創(chuàng)建前面已經(jīng)介紹了,那么咱們就看看SqlSession的CRUD方法了,為了省事,還是就選擇其中的一個(gè)方法來(lái)做分析吧。這兒,咱們選擇了selectList方法:

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        MappedStatement ms = configuration.getMappedStatement(statement);
        //CRUD實(shí)際上是交給Excetor去處理, excutor其實(shí)也只是穿了個(gè)馬甲而已,小樣,別以為穿個(gè)馬甲我就不認(rèn)識(shí)你嘞!
        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();
    }
}
  • CacheExecutor: CacheExecutor有一個(gè)重要屬性delegate,它保存的是某類(lèi)普通的Executor,值在構(gòu)照時(shí)傳入。執(zhí)行數(shù)據(jù)庫(kù)update操作時(shí),它直接調(diào)用delegate的update方法,執(zhí)行query方法時(shí)先嘗試從cache中取值,取不到再調(diào)用delegate的查詢方法,并將查詢結(jié)果存入cache中。代碼如下:
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds,ResultHandler resultHandler) throws SQLException {  
      if (ms != null) {  
          Cache cache = ms.getCache();  
          if (cache != null) {  
              flushCacheIfRequired(ms);  
              cache.getReadWriteLock().readLock().lock();  
              try {  
                  if (ms.isUseCache() && resultHandler ==null) {  
                      CacheKey key = createCacheKey(ms, parameterObject, rowBounds);  
                      final List cachedList = (List)cache.getObject(key);  
                      if (cachedList != null) {  
                          return cachedList;  
                      } else {  
                          List list = delegate.query(ms,parameterObject, rowBounds, resultHandler);  
                          tcm.putObject(cache,key, list);  
                          return list;  
                      }  
                  } else {  
                      return delegate.query(ms,parameterObject, rowBounds, resultHandler);  
                  }  
              } finally {  
                  cache.getReadWriteLock().readLock().unlock();  
              }
          }  
      }  
      return delegate.query(ms,parameterObject, rowBounds, resultHandler);  
}
  • 普通Executor: 有3類(lèi),他們都繼承于BaseExecutor,BatchExecutor專(zhuān)門(mén)用于執(zhí)行批量sql操作,ReuseExecutor會(huì)重用statement執(zhí)行sql操作,SimpleExecutor只是簡(jiǎn)單執(zhí)行sql沒(méi)有什么特別的。下面以SimpleExecutor為例:
public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,ResultHandler resultHandler) throws SQLException {  
       Statement stmt = null;  
       try {  
           Configuration configuration = ms.getConfiguration();  
           StatementHandler handler = configuration.newStatementHandler(this, ms,parameter, rowBounds,resultHandler);  
           stmt =prepareStatement(handler);  
           return handler.query(stmt, resultHandler);  
       } finally {  
           closeStatement(stmt);  
      }  
} 

然后,通過(guò)一層一層的調(diào)用,最終會(huì)來(lái)到doQuery方法, 這兒咱們就隨便找個(gè)Excutor看看doQuery方法的實(shí)現(xiàn)吧,我這兒選擇了SimpleExecutor:

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);
         stmt = prepareStatement(handler, ms.getStatementLog());
         //StatementHandler封裝了Statement, 讓 StatementHandler 去處理
         return handler.<E>query(stmt, resultHandler);
     } finally {
         closeStatement(stmt);
     }
}
  • Mybatis內(nèi)置的ExecutorType有3種,默認(rèn)的是simple,該模式下它為每個(gè)語(yǔ)句的執(zhí)行創(chuàng)建一個(gè)新的預(yù)處理語(yǔ)句,單條提交sql;
  • 而batch模式重復(fù)使用已經(jīng)預(yù)處理的語(yǔ)句,并且批量執(zhí)行所有更新語(yǔ)句,顯然batch性能將更優(yōu);但batch模式也有自己的問(wèn)題,比如在Insert操作時(shí),在事務(wù)沒(méi)有提交之前,是沒(méi)有辦法獲取到自增的id,這在某型情形下是不符合業(yè)務(wù)要求的;

通過(guò)走碼和研讀spring相關(guān)文件發(fā)現(xiàn),在同一事務(wù)中batch模式和simple模式之間無(wú)法轉(zhuǎn)換,由于本項(xiàng)目一開(kāi)始選擇了simple模式,所以碰到需要批量更新時(shí),只能在單獨(dú)的事務(wù)中進(jìn)行;

在代碼中使用batch模式可以使用以下方式:

//從spring注入原有的sqlSessionTemplate
@Autowired
private SqlSessionTemplate sqlSessionTemplate;
 
public void testInsertBatchByTrue() {
    //新獲取一個(gè)模式為BATCH,自動(dòng)提交為false的session
    //如果自動(dòng)提交設(shè)置為true,將無(wú)法控制提交的條數(shù),改為最后統(tǒng)一提交,可能導(dǎo)致內(nèi)存溢出
    SqlSession session = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
    //通過(guò)新的session獲取mapper
    fooMapper = session.getMapper(FooMapper.class);
    int size = 10000;
    try {
        for (int i = 0; i < size; i++) {
            Foo foo = new Foo();
            foo.setName(String.valueOf(System.currentTimeMillis()));
            fooMapper.insert(foo);
            if (i % 1000 == 0 || i == size - 1) {
                //手動(dòng)每1000個(gè)一提交,提交后無(wú)法回滾
                session.commit();
                //清理緩存,防止溢出
                session.clearCache();
            }
        }
    } catch (Exception e) {
        //沒(méi)有提交的數(shù)據(jù)可以回滾
        session.rollback();
    } finally {
        session.close();
    }
}

上述代碼沒(méi)有使用spring的事務(wù),改手動(dòng)控制,如果和原spring事務(wù)一起使用,將無(wú)法回滾,必須注意,最好單獨(dú)使用;

StatementHandler

可以看出,Executor本質(zhì)上也是個(gè)甩手掌柜,具體的事情原來(lái)是StatementHandler來(lái)完成的。當(dāng)Executor將指揮棒交給StatementHandler后,接下來(lái)的工作就是StatementHandler的事了。我們先看看StatementHandler是如何創(chuàng)建的:

public StatementHandler newStatementHandler(Executor executor, MappedStatementmappedStatement,  
        ObjectparameterObject, RowBounds rowBounds, ResultHandler resultHandler) {  
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement,parameterObject,rowBounds, resultHandler);  
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);  
    return statementHandler;
}  
  • 可以看到每次創(chuàng)建的StatementHandler都是RoutingStatementHandler,它只是一個(gè)分發(fā)者,他一個(gè)屬性delegate用于指定用哪種具體的StatementHandler
  • 可選的StatementHandlerSimpleStatementHandler、PreparedStatementHandlerCallableStatementHandler三種。選用哪種在mapper配置文件的每個(gè)statement里指定,默認(rèn)的是PreparedStatementHandler。同時(shí)還要注意到StatementHandler是可以被攔截器攔截的,和Executor一樣,被攔截器攔截后的對(duì)像是一個(gè)代理對(duì)象。
  • 由于mybatis沒(méi)有實(shí)現(xiàn)數(shù)據(jù)庫(kù)的物理分頁(yè),眾多物理分頁(yè)的實(shí)現(xiàn)都是在這個(gè)地方使用攔截器實(shí)現(xiàn)的。

StatementHandler創(chuàng)建后需要執(zhí)行一些初始操作,比如statement的開(kāi)啟和參數(shù)設(shè)置、對(duì)于PreparedStatement還需要執(zhí)行參數(shù)的設(shè)置操作等。代碼如下:

private Statement prepareStatement(StatementHandler handler) throws SQLException {  
    Statement stmt;  
    Connection connection = transaction.getConnection();  
    stmt =handler.prepare(connection);  
    handler.parameterize(stmt);  
    return stmt;  
}

statement的開(kāi)啟和參數(shù)設(shè)置沒(méi)什么特別的地方,handler.parameterize倒是可以看看是怎么回事。handler.parameterize通過(guò)調(diào)用ParameterHandler的setParameters完成參數(shù)的設(shè)置,ParameterHandler隨著StatementHandler的創(chuàng)建而創(chuàng)建,默認(rèn)的實(shí)現(xiàn)是DefaultParameterHandler

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {  
    ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement,parameterObject,boundSql);  
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);  
    return parameterHandler;  
}

Executor和StatementHandler一樣,ParameterHandler也是可以被攔截的。DefaultParameterHandler里設(shè)置參數(shù)的代碼如下:

public void setParameters(PreparedStatement ps) throws SQLException {  
    ErrorContext.instance().activity("settingparameters").object(mappedStatement.getParameterMap().getId());  
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();  
    if(parameterMappings != null) {  
        MetaObject metaObject = parameterObject == null ? null :configuration.newMetaObject(parameterObject);  
        for (int i = 0; i< parameterMappings.size(); i++) {  
            ParameterMapping parameterMapping = parameterMappings.get(i);  
            if(parameterMapping.getMode() != ParameterMode.OUT) {  
                Object value;  
                String propertyName = parameterMapping.getProperty();  
                PropertyTokenizer prop = newPropertyTokenizer(propertyName);  
                if (parameterObject == null) {  
                    value = null;  
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())){  
                    value = parameterObject;  
                } else if (boundSql.hasAdditionalParameter(propertyName)){  
                    value = boundSql.getAdditionalParameter(propertyName);  
                } else if(propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)  
                        && boundSql.hasAdditionalParameter(prop.getName())){  
                    value = boundSql.getAdditionalParameter(prop.getName());  
                    if (value != null) {  
                        value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));  
                    }  
                } else {  
                    value = metaObject == null ? null :metaObject.getValue(propertyName);  
                }  
                TypeHandler typeHandler = parameterMapping.getTypeHandler();  
                if (typeHandler == null) {  
                   throw new ExecutorException("Therewas no TypeHandler found for parameter " + propertyName  + " of statement " + mappedStatement.getId());  
                }  
                typeHandler.setParameter(ps, i + 1, value,parameterMapping.getJdbcType());  
            }  
  
        }  
  
    }  
}  

這里面最重要的一句其實(shí)就是最后一句代碼,它的作用是用合適的TypeHandler完成參數(shù)的設(shè)置。那么什么是合適的TypeHandler呢,它又是如何決斷出來(lái)的呢?BaseStatementHandler的構(gòu)造方法里有這么一句:

this.boundSql= mappedStatement.getBoundSql(parameterObject);

它觸發(fā)了sql 的解析,在解析sql的過(guò)程中,TypeHandler也被決斷出來(lái)了,決斷的原則就是根據(jù)參數(shù)的類(lèi)型和參數(shù)對(duì)應(yīng)的JDBC類(lèi)型決定使用哪個(gè)TypeHandler。比如:參數(shù)類(lèi)型是String的話就用StringTypeHandler,參數(shù)類(lèi)型是整數(shù)的話就用IntegerTypeHandler等。

參數(shù)設(shè)置完畢后,執(zhí)行數(shù)據(jù)庫(kù)操作(update或query)。如果是query最后還有個(gè)查詢結(jié)果的處理過(guò)程。

接下來(lái),咱們看看StatementHandler 的一個(gè)實(shí)現(xiàn)類(lèi) PreparedStatementHandler(這也是我們最常用的,封裝的是PreparedStatement), 看看它使怎么去處理的:

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    // 到此,原形畢露, PreparedStatement, 這個(gè)大家都已經(jīng)滾瓜爛熟了吧
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    // 結(jié)果交給了ResultSetHandler 去處理
    return resultSetHandler.<E> handleResultSets(ps);
}

結(jié)果處理使用ResultSetHandler來(lái)完成,默認(rèn)的ResultSetHandlerFastResultSetHandler,它在創(chuàng)建StatementHandler時(shí)一起創(chuàng)建,代碼如下:

public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement,  
RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {  
    ResultSetHandler resultSetHandler = mappedStatement.hasNestedResultMaps() ? newNestedResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds): new FastResultSetHandler(executor,mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);  
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);  
    return resultSetHandler;  
}

可以看出ResultSetHandler也是可以被攔截的,可以編寫(xiě)自己的攔截器改變ResultSetHandler的默認(rèn)行為。ResultSetHandler內(nèi)部一條記錄一條記錄的處理,在處理每條記錄的每一列時(shí)會(huì)調(diào)用TypeHandler轉(zhuǎn)換結(jié)果,如下:

protected boolean applyAutomaticMappings(ResultSet rs,List<String> unmappedColumnNames,MetaObject metaObject)throws SQLException{
    boolean foundValues=false;
    for(String columnName:unmappedColumnNames){
        final String property=metaObject.findProperty(columnName);
        if(property!=null){
            final ClasspropertyType=metaObject.getSetterType(property);
            if(typeHandlerRegistry.hasTypeHandler(propertyType)){
                final TypeHandler typeHandler=typeHandlerRegistry.getTypeHandler(propertyType);
                final Object value=typeHandler.getResult(rs,columnName);
                if(value!=null){
                    metaObject.setValue(property,value);
                    foundValues=true;
                }
            }
        }
    }
    return foundValues;
}

從代碼里可以看到,決斷TypeHandler使用的是結(jié)果參數(shù)的屬性類(lèi)型。因此我們?cè)诙x作為結(jié)果的對(duì)象的屬性時(shí)一定要考慮與數(shù)據(jù)庫(kù)字段類(lèi)型的兼容性。到此,一次sql的執(zhí)行流程就完了。

到此這篇關(guān)于MyBatis SqlSession執(zhí)行流程小結(jié)的文章就介紹到這了,更多相關(guān)MyBatis SqlSession執(zhí)行流程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關(guān)文章

  • JAVA自定義注解實(shí)現(xiàn)接口/ip限流的示例代碼

    JAVA自定義注解實(shí)現(xiàn)接口/ip限流的示例代碼

    本文主要介紹了JAVA自定義注解實(shí)現(xiàn)接口/ip限流的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • JPA的多表復(fù)雜查詢的方法示例

    JPA的多表復(fù)雜查詢的方法示例

    這篇文章主要介紹了JPA的多表復(fù)雜查詢的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • SpringBoot_Cache自定義使用SimpleCacheManager方式

    SpringBoot_Cache自定義使用SimpleCacheManager方式

    這篇文章主要介紹了SpringBoot_Cache自定義使用SimpleCacheManager方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Java中同步與并發(fā)用法分析

    Java中同步與并發(fā)用法分析

    這篇文章主要介紹了Java中同步與并發(fā)用法,較為詳細(xì)的分析了java同步與并發(fā)所涉及的相關(guān)類(lèi)與使用技巧,需要的朋友可以參考下
    2015-06-06
  • Spring依賴注入(DI)兩種方式的示例詳解

    Spring依賴注入(DI)兩種方式的示例詳解

    這篇文章主要介紹了Spring依賴注入(DI)的兩種方式:setter注入和構(gòu)造器注入。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2022-06-06
  • Java 控制流程、大數(shù)值、數(shù)組

    Java 控制流程、大數(shù)值、數(shù)組

    這篇文章主要給大家介紹的是Java 控制流程、大數(shù)值、數(shù)組的一些相關(guān)自來(lái)哦,感興趣的小伙伴可以參考下面文章的具體內(nèi)容,希望文章對(duì)你有所幫助
    2021-10-10
  • springboot命令行啟動(dòng)的方法詳解

    springboot命令行啟動(dòng)的方法詳解

    這篇文章主要介紹了springboot命令行啟動(dòng)的方法,本文通過(guò)兩種方法給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • java基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)全局唯一ID的示例

    java基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)全局唯一ID的示例

    本文主要介紹了java基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)全局唯一ID的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Java常用類(lèi)庫(kù)StringBuffer,Runtime,日期操作類(lèi)等類(lèi)庫(kù)總結(jié)

    Java常用類(lèi)庫(kù)StringBuffer,Runtime,日期操作類(lèi)等類(lèi)庫(kù)總結(jié)

    這篇文章主要介紹了Java常用類(lèi)庫(kù)StringBuffer,Runtime,日期操作類(lèi)等類(lèi)庫(kù)總結(jié),需要的朋友可以參考下
    2020-02-02
  • SpringMVC+EasyUI實(shí)現(xiàn)頁(yè)面左側(cè)導(dǎo)航菜單功能

    SpringMVC+EasyUI實(shí)現(xiàn)頁(yè)面左側(cè)導(dǎo)航菜單功能

    這篇文章主要介紹了SpringMVC+EasyUI實(shí)現(xiàn)頁(yè)面左側(cè)導(dǎo)航菜單功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-09-09

最新評(píng)論