<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Mybatis數(shù)據(jù)源結(jié)構(gòu)解析

          共 14544字,需瀏覽 30分鐘

           ·

          2020-12-06 02:11


          點(diǎn)擊上方藍(lán)字關(guān)注我!





          正文共:?14030字?27

          預(yù)計(jì)閱讀時(shí)間:?36分鐘


          對于 ORM 框架而言,數(shù)據(jù)源的組織是一個(gè)非常重要的一部分,這直接影響到框架的性能問題。本文將通過對 MyBatis 框架的數(shù)據(jù)源結(jié)構(gòu)進(jìn)行詳盡的分析,找出什么時(shí)候創(chuàng)建 Connection ,并且深入解析 MyBatis 的連接池。


          本章的組織結(jié)構(gòu):

          • 零、什么是連接池和線程池
          • 一、MyBatis 數(shù)據(jù)源 DataSource 分類
          • 二、數(shù)據(jù)源 DataSource 的創(chuàng)建過程
          • 三、 DataSource 什么時(shí)候創(chuàng)建 Connection 對象
          • 四、不使用連接池的 UnpooledDataSource
          • 五、使用了連接池的 PooledDataSource

          連接池和線程池

          連接池:(降低物理連接損耗)

          • 1、連接池是面向數(shù)據(jù)庫連接的
          • 2、連接池是為了優(yōu)化數(shù)據(jù)庫連接資源
          • 3、連接池有點(diǎn)類似在客戶端做優(yōu)化

          數(shù)據(jù)庫連接是一項(xiàng)有限的昂貴資源,一個(gè)數(shù)據(jù)庫連接對象均對應(yīng)一個(gè)物理數(shù)據(jù)庫連接,每次操作都打開一個(gè)物理連接,使用完都關(guān)閉連接,這樣造成系統(tǒng)的性能低下。數(shù)據(jù)庫連接池的解決方案是在應(yīng)用程序啟動(dòng)時(shí)建立足夠的數(shù)據(jù)庫連接,并將這些連接組成一個(gè)連接池,由應(yīng)用程序動(dòng)態(tài)地對池中的連接進(jìn)行申請、使用和釋放。對于多于連接池中連接數(shù)的并發(fā)請求,應(yīng)該在請求隊(duì)列中排隊(duì)等待。并且應(yīng)用程序可以根據(jù)池中連接的使用率,動(dòng)態(tài)增加或減少池中的連接數(shù)。


          線程池:(降低線程創(chuàng)建銷毀損耗)

          • 1、線程池是面向后臺程序的
          • 2、線程池是是為了提高內(nèi)存和CPU效率
          • 3、線程池有點(diǎn)類似于在服務(wù)端做優(yōu)化

          線程池是一次性創(chuàng)建一定數(shù)量的線程(應(yīng)該可以配置初始線程數(shù)量的),當(dāng)用請求過來不用去創(chuàng)建新的線程,直接使用已創(chuàng)建的線程,使用后又放回到線程池中。避免了頻繁創(chuàng)建線程,及銷毀線程的系統(tǒng)開銷,提高是內(nèi)存和CPU效率。

          相同點(diǎn):

          都是事先準(zhǔn)備好資源,避免頻繁創(chuàng)建和銷毀的代價(jià)。

          數(shù)據(jù)源的分類

          在Mybatis體系中,分為3DataSource

          ?

          打開Mybatis源碼找到datasource包,可以看到3個(gè)子package

          ?
          • UNPOOLED ? ?不使用連接池的數(shù)據(jù)源

          • POOLED ? ? ?使用連接池的數(shù)據(jù)源

          • JNDI ? ? ? ?使用JNDI實(shí)現(xiàn)的數(shù)據(jù)源

          ?

          MyBatis內(nèi)部分別定義了實(shí)現(xiàn)了java.sql.DataSource接口的UnpooledDataSource,PooledDataSource類來表示UNPOOLED、POOLED類型的數(shù)據(jù)源。如下圖所示:

          ?
          • PooledDataSource和UnpooledDataSrouce都實(shí)現(xiàn)了java.sql.DataSource接口.
          • PooledDataSource持有一個(gè)UnPooledDataSource的引用,當(dāng)PooledDataSource要?jiǎng)?chuàng)建Connection實(shí)例時(shí),實(shí)際還是通過UnPooledDataSource來創(chuàng)建的.(PooledDataSource)只是提供一種緩存連接池機(jī)制.
          ?

          JNDI類型的數(shù)據(jù)源DataSource,則是通過JNDI上下文中取值。

          ?

          數(shù)據(jù)源 DataSource 的創(chuàng)建過程

          ?

          在mybatis的XML配置文件中,使用元素來配置數(shù)據(jù)源:

          ?

          <dataSource?type="POOLED">?//這里?type?屬性的取值就是為POOLED、UNPOOLED、JNDI
          ??<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>

          MyBatis在初始化時(shí),解析此文件,根據(jù)的type屬性來創(chuàng)建相應(yīng)類型的的數(shù)據(jù)源DataSource,即:

          • type=”POOLED” ? ? :創(chuàng)建PooledDataSource實(shí)例

          • type=”UNPOOLED” ? :創(chuàng)建UnpooledDataSource實(shí)例

          • type=”JNDI” ? ? ? :從JNDI服務(wù)上查找DataSource實(shí)例


            Mybatis是通過工廠模式來創(chuàng)建數(shù)據(jù)源對象的 我們來看看源碼:

            public?interface?DataSourceFactory?{

            void?setProperties(Properties?props);

            DataSource?getDataSource();//生產(chǎn)DataSource

            }

            上述3種類型的數(shù)據(jù)源,對應(yīng)有自己的工廠模式,都實(shí)現(xiàn)了這個(gè)DataSourceFactory

          ?

          MyBatis創(chuàng)建了DataSource實(shí)例后,會將其放到Configuration對象內(nèi)的Environment對象中, 供以后使用。

          ?
          ?

          注意dataSource 此時(shí)只會保存好配置信息.連接池此時(shí)并沒有創(chuàng)建好連接.只有當(dāng)程序在調(diào)用操作數(shù)據(jù)庫的方法時(shí),才會初始化連接.

          ?

          DataSource什么時(shí)候創(chuàng)建Connection對象

          我們需要?jiǎng)?chuàng)建SqlSession對象并需要執(zhí)行SQL語句時(shí),這時(shí)候MyBatis才會去調(diào)用dataSource對象來創(chuàng)建java.sql.Connection對象。也就是說,java.sql.Connection對象的創(chuàng)建一直延遲到執(zhí)行SQL語句的時(shí)候。

          例子:

          ??@Test
          ??public?void?testMyBatisBuild()?throws?IOException?{
          ??????Reader?reader?=?Resources.getResourceAsReader("mybatis-config.xml");
          ??????SqlSessionFactory?factory?=?new?SqlSessionFactoryBuilder().build(reader);
          ??????SqlSession?sqlSession?=?factory.openSession();
          ??????TestMapper?mapper?=?sqlSession.getMapper(TestMapper.class);
          ??????Ttest?one?=?mapper.getOne(1L);//直到這一行,才會去創(chuàng)建一個(gè)數(shù)據(jù)庫連接!
          ??????System.out.println(one);
          ??????sqlSession.close();
          ??}

          口說無憑,跟進(jìn)源碼看看他們是在什么時(shí)候創(chuàng)建的...

          跟進(jìn)源碼,驗(yàn)證Datasource 和Connection對象創(chuàng)建時(shí)機(jī)

          驗(yàn)證Datasource創(chuàng)建時(shí)機(jī)
          • 上面我們已經(jīng)知道,pooled數(shù)據(jù)源實(shí)際上也是使用的unpooled的實(shí)例,那么我們在UnpooledDataSourceFactory的 getDataSource方法的源碼中做一些修改 并運(yùn)行測試用例:
          @Override
          public?DataSource?getDataSource()?{//此方法是UnpooledDataSourceFactory實(shí)現(xiàn)DataSourceFactory復(fù)寫
          ??System.out.println("創(chuàng)建了數(shù)據(jù)源");
          ??System.out.println(dataSource.toString());
          ??return?dataSource;
          }
          ?

          結(jié)論:在創(chuàng)建完SqlsessionFactory時(shí),DataSource實(shí)例就創(chuàng)建了.

          ?

          驗(yàn)證Connection創(chuàng)建時(shí)機(jī)

          首先我們先查出現(xiàn)在數(shù)據(jù)庫的所有連接數(shù),在數(shù)據(jù)庫中執(zhí)行

          SELECT?*?FROM?performance_schema.hosts;
          ?

          返回?cái)?shù)據(jù): ?顯示當(dāng)前連接數(shù)為1,總連接數(shù)70

          ?
          show?full?processlist;?//顯示所有的任務(wù)列表
          ?

          返回:當(dāng)前只有一個(gè)查詢的連接在運(yùn)行

          ?

          重新啟動(dòng)項(xiàng)目,在運(yùn)行到需要執(zhí)行實(shí)際的sql操作時(shí),可以看到他已經(jīng)被代理增強(qiáng)了

          ?

          直到此時(shí),連接數(shù)還是沒有變,說明連接還沒有創(chuàng)建,我們接著往下看.

          ?
          ?

          我們按F7進(jìn)入方法,可以看到,他被代理,,這時(shí)候會執(zhí)行到之前的代理方法中調(diào)用invoke方法.這里有一個(gè)判斷,但是并不成立,于是進(jìn)入cachedInvoker(method).invoke()方法代理執(zhí)行一下操作

          ?

          cachedInvoker(method).invoke()方法

          ??
          ????@Override
          ????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args,?SqlSession?sqlSession)?throws?Throwable?{
          ??????return?mapperMethod.execute(sqlSession,?args);
          ????}
          ?

          繼續(xù)F7進(jìn)入方法,由于我們是單條查詢select 所以會case進(jìn)入select塊中的selectOne

          ?

          繼續(xù)F7

          繼續(xù)F7

          ?

          通過configuration.getMappedStatement獲取MappedStatement

          ?
          ?

          單步步過,F8后,進(jìn)入executor.query方法

          ?
          @Override
          public??List?query(MappedStatement?ms,?Object?parameterObject,?RowBounds?rowBounds,?ResultHandler?resultHandler)?throws?SQLException?{
          ??BoundSql?boundSql?=?ms.getBoundSql(parameterObject);
          ??CacheKey?key?=?createCacheKey(ms,?parameterObject,?rowBounds,?boundSql);
          ??return?query(ms,?parameterObject,?rowBounds,?resultHandler,?key,?boundSql);
          }
          ?

          繼續(xù)走到底,F7進(jìn)入query方法

          ?
          ?

          此時(shí),會去緩存中查詢,這里的緩存是二級緩存對象 ,生命周期是mapper級別的(一級緩存是一個(gè)session級別的),因?yàn)槲覀兇藭r(shí)是第一次運(yùn)行程序,所以肯定為Null,這時(shí)候會直接去查詢,調(diào)用delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql)方法,F7進(jìn)入這個(gè)方法

          ?
          ?

          二級緩存沒有獲取到,又去查詢了一級緩存,發(fā)現(xiàn)一級緩存也沒有,這個(gè)時(shí)候,才去查數(shù)據(jù)庫

          ?

          queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);//沒有緩存則去db查

          ?

          F7進(jìn)入queryFromDatabase方法.看到是一些對一級緩存的操作,我們主要看doQuery方法F7進(jìn)入它.

          ?
          ?

          可以看到它準(zhǔn)備了一個(gè)空的Statement

          ?
          ?

          我們F7跟進(jìn)看一下prepareStatement方法 ,發(fā)現(xiàn)他調(diào)用了getConnection,哎!有點(diǎn)眼熟了,繼續(xù)F7進(jìn)入getConnection()方法,

          ?
          ?

          又是一個(gè)getConnection()....繼續(xù)F7進(jìn)入transaction.getConnection()方法

          ?
          ?

          又是一個(gè)getConnection()方法.判斷connection是否為空.為空openConnection()否則直接返回connection;我們F7繼續(xù)跟進(jìn)openConnection()方法

          ?
          ??protected?void?openConnection()?throws?SQLException?{
          ??if?(log.isDebugEnabled())?{
          ????log.debug("Opening?JDBC?Connection");
          ??}
          ??connection?=?dataSource.getConnection();//最終獲取連接的地方在這句.
          ??if?(level?!=?null)?{
          ????connection.setTransactionIsolation(level.getLevel());//設(shè)置隔離等級
          ??}
          ??setDesiredAutoCommit(autoCommit);//是否自動(dòng)提交,默認(rèn)false,update不會提交到數(shù)據(jù)庫,需要手動(dòng)commit
          }

          dataSource.getConnection()執(zhí)行完,至此一個(gè)connection才創(chuàng)建完成. 我們驗(yàn)證一下 在dataSource.getConnection()時(shí)打一下斷點(diǎn).

          此時(shí)數(shù)據(jù)庫中的連接數(shù)依然沒變 還是1

          我們按F8 執(zhí)行一步

          在控制臺可以看到connection = com.mysql.jdbc.JDBC4Connection@1500b2f3 實(shí)例創(chuàng)建完畢 我們再去數(shù)據(jù)庫中看看連接數(shù)

          兩個(gè)連接分別是:

          不使用連接池的 UnpooledDataSource

          ?

          當(dāng)的type屬性被配置成了”UNPOOLED”,MyBatis首先會實(shí)例化一個(gè)UnpooledDataSourceFactory工廠實(shí)例,然后通過.getDataSource()方法返回一個(gè)UnpooledDataSource實(shí)例對象引用,我們假定為dataSource。使用UnpooledDataSource的getConnection(),每調(diào)用一次就會產(chǎn)生一個(gè)新的Connection實(shí)例對象。

          ?

          UnPooledDataSource的getConnection()方法實(shí)現(xiàn)如下:

          /*
          UnpooledDataSource的getConnection()實(shí)現(xiàn)
          */

          public?Connection?getConnection()?throws?SQLException
          {
          ??return?doGetConnection(username,?password);
          }

          private?Connection?doGetConnection(String?username,?String?password)?throws?SQLException
          {
          ??//封裝username和password成properties
          ??Properties?props?=?new?Properties();
          ??if?(driverProperties?!=?null)
          ??{
          ??????props.putAll(driverProperties);
          ??}
          ??if?(username?!=?null)
          ??{
          ??????props.setProperty("user",?username);
          ??}
          ??if?(password?!=?null)
          ??{
          ??????props.setProperty("password",?password);
          ??}
          ??return?doGetConnection(props);
          }

          /*
          *??獲取數(shù)據(jù)連接
          */

          private?Connection?doGetConnection(Properties?properties)?throws?SQLException
          {
          ??//1.初始化驅(qū)動(dòng)
          ??initializeDriver();
          ??//2.從DriverManager中獲取連接,獲取新的Connection對象
          ??Connection?connection?=?DriverManager.getConnection(url,?properties);
          ??//3.配置connection屬性
          ??configureConnection(connection);
          ??return?connection;
          }
          ?

          UnpooledDataSource會做以下幾件事情:

          ?

            1. 初始化驅(qū)動(dòng):? ?判斷driver驅(qū)動(dòng)是否已經(jīng)加載到內(nèi)存中,如果還沒有加載,則會動(dòng)態(tài)地加載driver類,并實(shí)例化一個(gè)Driver對象,使用DriverManager.registerDriver()方法將其注冊到內(nèi)存中,以供后續(xù)使用。

            1. 創(chuàng)建Connection對象:? ?使用DriverManager.getConnection()方法創(chuàng)建連接。

            1. 配置Connection對象:? ?設(shè)置是否自動(dòng)提交autoCommit和隔離級別isolationLevel。

            1. 返回Connection對象。
          ?

          我們每調(diào)用一次getConnection()方法,都會通過DriverManager.getConnection()返回新的java.sql.Connection實(shí)例,這樣當(dāng)然對于資源是一種浪費(fèi),為了防止重復(fù)的去創(chuàng)建和銷毀連接,于是引入了連接池的概念.

          ?

          使用了連接池的 PooledDataSource

          要理解連接池,首先要了解它對于connection的容器,它使用PoolState容器來管理所有的conncetion

          在PoolState中,它將connection分為兩種狀態(tài),空閑狀態(tài)(idle)活動(dòng)狀態(tài)(active),他們分別被存儲到PoolState容器內(nèi)的idleConnectionsactiveConnections兩個(gè)ArrayList中

          • idleConnections:空閑(idle)狀態(tài)PooledConnection對象被放置到此集合中,表示當(dāng)前閑置的沒有被使用的PooledConnection集合,調(diào)用PooledDataSource的getConnection()方法時(shí),會優(yōu)先從此集合中取PooledConnection對象。當(dāng)用完一個(gè)java.sql.Connection對象時(shí),MyBatis會將其包裹成PooledConnection對象放到此集合中。

          • activeConnections:活動(dòng)(active)狀態(tài)的PooledConnection對象被放置到名為activeConnections的ArrayList中,表示當(dāng)前正在被使用的PooledConnection集合,調(diào)用PooledDataSource的getConnection()方法時(shí),會優(yōu)先從idleConnections集合中取PooledConnection對象,如果沒有,則看此集合是否已滿,如果未滿,PooledDataSource會創(chuàng)建出一個(gè)PooledConnection,添加到此集合中,并返回。

          從連接池中獲取一個(gè)連接對象的過程

          下面讓我們看一下PooledDataSource 的popConnection方法獲取Connection對象的實(shí)現(xiàn):

          private?PooledConnection?popConnection(String?username,?String?password)?throws?SQLException?{
          ????boolean?countedWait?=?false;
          ????PooledConnection?conn?=?null;
          ????long?t?=?System.currentTimeMillis();
          ????int?localBadConnectionCount?=?0;

          ????while?(conn?==?null)?{
          ??????synchronized?(state)?{//給state對象加鎖
          ????????if?(!state.idleConnections.isEmpty())?{//如果空閑列表不空,就從空閑列表中拿connection
          ??????????//?Pool?has?available?connection
          ??????????conn?=?state.idleConnections.remove(0);//拿出空閑列表中的第一個(gè),去驗(yàn)證連接是否還有效
          ??????????if?(log.isDebugEnabled())?{
          ????????????log.debug("Checked?out?connection?"?+?conn.getRealHashCode()?+?"?from?pool.");
          ??????????}
          ????????}?else?{
          ??????????//?空閑連接池中沒有可用的連接,就來看看活躍連接列表中是否有..先判斷活動(dòng)連接總數(shù)?是否小于?最大可用的活動(dòng)連接數(shù)
          ??????????if?(state.activeConnections.size()?????????????//?如果連接數(shù)小于list.size?直接創(chuàng)建新的連接.
          ????????????conn?=?new?PooledConnection(dataSource.getConnection(),?this);
          ????????????if?(log.isDebugEnabled())?{
          ??????????????log.debug("Created?connection?"?+?conn.getRealHashCode()?+?".");
          ????????????}
          ??????????}?else?{
          ????????????//?此時(shí)連接數(shù)也滿了,不能創(chuàng)建新的連接.?找到最老的那個(gè),檢查它是否過期
          ????????????//計(jì)算它的校驗(yàn)時(shí)間,如果校驗(yàn)時(shí)間大于連接池規(guī)定的最大校驗(yàn)時(shí)間,則認(rèn)為它已經(jīng)過期了
          ????????????//?利用這個(gè)PoolConnection內(nèi)部的realConnection重新生成一個(gè)PooledConnection
          ????????????PooledConnection?oldestActiveConnection?=?state.activeConnections.get(0);
          ????????????long?longestCheckoutTime?=?oldestActiveConnection.getCheckoutTime();
          ????????????if?(longestCheckoutTime?>?poolMaximumCheckoutTime)?{
          ??????????????//?可以要求過期這個(gè)連接.
          ??????????????state.claimedOverdueConnectionCount++;
          ??????????????state.accumulatedCheckoutTimeOfOverdueConnections?+=?longestCheckoutTime;
          ??????????????state.accumulatedCheckoutTime?+=?longestCheckoutTime;
          ??????????????state.activeConnections.remove(oldestActiveConnection);
          ??????????????if?(!oldestActiveConnection.getRealConnection().getAutoCommit())?{
          ????????????????try?{
          ??????????????????oldestActiveConnection.getRealConnection().rollback();
          ????????????????}?catch?(SQLException?e)?{
          ??????????????????/*
          ?????????????????????Just?log?a?message?for?debug?and?continue?to?execute?the?following
          ?????????????????????statement?like?nothing?happened.
          ?????????????????????Wrap?the?bad?connection?with?a?new?PooledConnection,?this?will?help
          ?????????????????????to?not?interrupt?current?executing?thread?and?give?current?thread?a
          ?????????????????????chance?to?join?the?next?competition?for?another?valid/good?database
          ?????????????????????connection.?At?the?end?of?this?loop,?bad?{@link?@conn}?will?be?set?as?null.
          ???????????????????*/

          ??????????????????log.debug("Bad?connection.?Could?not?roll?back");
          ????????????????}
          ??????????????}
          ??????????????conn?=?new?PooledConnection(oldestActiveConnection.getRealConnection(),?this);
          ??????????????conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
          ??????????????conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
          ??????????????oldestActiveConnection.invalidate();
          ??????????????if?(log.isDebugEnabled())?{
          ????????????????log.debug("Claimed?overdue?connection?"?+?conn.getRealHashCode()?+?".");
          ??????????????}
          ????????????}?else?{
          ??????????????//如果不能釋放,則必須等待
          ??????????????//?Must?wait
          ??????????????try?{
          ????????????????if?(!countedWait)?{
          ??????????????????state.hadToWaitCount++;
          ??????????????????countedWait?=?true;
          ????????????????}
          ????????????????if?(log.isDebugEnabled())?{
          ??????????????????log.debug("Waiting?as?long?as?"?+?poolTimeToWait?+?"?milliseconds?for?connection.");
          ????????????????}
          ????????????????long?wt?=?System.currentTimeMillis();
          ????????????????state.wait(poolTimeToWait);
          ????????????????state.accumulatedWaitTime?+=?System.currentTimeMillis()?-?wt;
          ??????????????}?catch?(InterruptedException?e)?{
          ????????????????break;
          ??????????????}
          ????????????}
          ??????????}
          ????????}
          ????????if?(conn?!=?null)?{
          ??????????//?ping?to?server?and?check?the?connection?is?valid?or?not
          ??????????if?(conn.isValid())?{//去驗(yàn)證連接是否還有效.
          ????????????if?(!conn.getRealConnection().getAutoCommit())?{
          ??????????????conn.getRealConnection().rollback();
          ????????????}
          ????????????conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(),?username,?password));
          ????????????conn.setCheckoutTimestamp(System.currentTimeMillis());
          ????????????conn.setLastUsedTimestamp(System.currentTimeMillis());
          ????????????state.activeConnections.add(conn);
          ????????????state.requestCount++;
          ????????????state.accumulatedRequestTime?+=?System.currentTimeMillis()?-?t;
          ??????????}?else?{
          ????????????if?(log.isDebugEnabled())?{
          ??????????????log.debug("A?bad?connection?("?+?conn.getRealHashCode()?+?")?was?returned?from?the?pool,?getting?another?connection.");
          ????????????}
          ????????????state.badConnectionCount++;
          ????????????localBadConnectionCount++;
          ????????????conn?=?null;
          ????????????if?(localBadConnectionCount?>?(poolMaximumIdleConnections?+?poolMaximumLocalBadConnectionTolerance))?{
          ??????????????if?(log.isDebugEnabled())?{
          ????????????????log.debug("PooledDataSource:?Could?not?get?a?good?connection?to?the?database.");
          ??????????????}
          ??????????????throw?new?SQLException("PooledDataSource:?Could?not?get?a?good?connection?to?the?database.");
          ????????????}
          ??????????}
          ????????}
          ??????}

          ????}

          ????if?(conn?==?null)?{
          ??????if?(log.isDebugEnabled())?{
          ????????log.debug("PooledDataSource:?Unknown?severe?error?condition.??The?connection?pool?returned?a?null?connection.");
          ??????}
          ??????throw?new?SQLException("PooledDataSource:?Unknown?severe?error?condition.??The?connection?pool?returned?a?null?connection.");
          ????}

          ????return?conn;
          ??}
          ?

          如上所示,對于PooledDataSource的getConnection()方法內(nèi),先是調(diào)用類PooledDataSource的popConnection()方法返回了一個(gè)PooledConnection對象,然后調(diào)用了PooledConnection的getProxyConnection()來返回Connection對象。

          ?

          復(fù)用連接的過程

          ?

          如果我們使用了連接池,我們在用完了Connection對象時(shí),需要將它放在連接池中,該怎樣做呢?如果讓我們來想的話,應(yīng)該是通過代理Connection對象,在調(diào)用close時(shí),并不真正關(guān)閉,而是丟到管理連接的容器中去. 要驗(yàn)證這個(gè)想法 那么 來看看Mybatis幫我們怎么實(shí)現(xiàn)復(fù)用連接的.

          ?
          class?PooledConnection?implements?InvocationHandler?{

          ??//......
          ??//所創(chuàng)建它的datasource引用
          ??private?PooledDataSource?dataSource;
          ??//真正的Connection對象
          ??private?Connection?realConnection;
          ??//代理自己的代理Connection
          ??private?Connection?proxyConnection;

          ??//......
          }
          ?

          PooledConenction實(shí)現(xiàn)了InvocationHandler接口,并且,proxyConnection對象也是根據(jù)這個(gè)它來生成的代理對象:

          ?
          public?PooledConnection(Connection?connection,?PooledDataSource?dataSource)?{
          ????this.hashCode?=?connection.hashCode();
          ????this.realConnection?=?connection;//真實(shí)連接
          ????this.dataSource?=?dataSource;
          ????this.createdTimestamp?=?System.currentTimeMillis();
          ????this.lastUsedTimestamp?=?System.currentTimeMillis();
          ????this.valid?=?true;
          ????this.proxyConnection?=?(Connection)?Proxy.newProxyInstance(Connection.class.getClassLoader(),?IFACES,?this);
          ??}
          ?

          實(shí)際上,我們調(diào)用PooledDataSource的getConnection()方法返回的就是這個(gè)proxyConnection對象。當(dāng)我們調(diào)用此proxyConnection對象上的任何方法時(shí),都會調(diào)用PooledConnection對象內(nèi)invoke()方法。讓我們看一下PooledConnection類中的invoke()方法定義:

          ?
          public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
          ????String?methodName?=?method.getName();
          ????//當(dāng)調(diào)用關(guān)閉的時(shí)候,回收此Connection到PooledDataSource中
          ????if?(CLOSE.hashCode()?==?methodName.hashCode()?&&?CLOSE.equals(methodName))?{
          ??????dataSource.pushConnection(this);
          ??????return?null;
          ????}?else?{
          ??????try?{
          ????????if?(!Object.class.equals(method.getDeclaringClass()))?{
          ??????????checkConnection();
          ????????}
          ????????return?method.invoke(realConnection,?args);
          ??????}?catch?(Throwable?t)?{
          ????????throw?ExceptionUtil.unwrapThrowable(t);
          ??????}
          ????}
          ??}
          ?

          結(jié)論:當(dāng)我們使用了pooledDataSource.getConnection()返回的Connection對象的close()方法時(shí),不會調(diào)用真正Connection的close()方法,而是將此Connection對象放到連接池中。調(diào)用dataSource.pushConnection(this)實(shí)現(xiàn)

          ?
          protected?void?pushConnection(PooledConnection?conn)?throws?SQLException?{

          ????synchronized?(state)?{
          ??????state.activeConnections.remove(conn);
          ??????if?(conn.isValid())?{
          ????????if?(state.idleConnections.size()???????????state.accumulatedCheckoutTime?+=?conn.getCheckoutTime();
          ??????????if?(!conn.getRealConnection().getAutoCommit())?{
          ????????????conn.getRealConnection().rollback();
          ??????????}
          ??????????PooledConnection?newConn?=?new?PooledConnection(conn.getRealConnection(),?this);
          ??????????state.idleConnections.add(newConn);
          ??????????newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
          ??????????newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
          ??????????conn.invalidate();
          ??????????if?(log.isDebugEnabled())?{
          ????????????log.debug("Returned?connection?"?+?newConn.getRealHashCode()?+?"?to?pool.");
          ??????????}
          ??????????state.notifyAll();
          ????????}?else?{
          ??????????state.accumulatedCheckoutTime?+=?conn.getCheckoutTime();
          ??????????if?(!conn.getRealConnection().getAutoCommit())?{
          ????????????conn.getRealConnection().rollback();
          ??????????}
          ??????????conn.getRealConnection().close();
          ??????????if?(log.isDebugEnabled())?{
          ????????????log.debug("Closed?connection?"?+?conn.getRealHashCode()?+?".");
          ??????????}
          ??????????conn.invalidate();
          ????????}
          ??????}?else?{
          ????????if?(log.isDebugEnabled())?{
          ??????????log.debug("A?bad?connection?("?+?conn.getRealHashCode()?+?")?attempted?to?return?to?the?pool,?discarding?connection.");
          ????????}
          ????????state.badConnectionCount++;
          ??????}
          ????}
          ??}


          瀏覽 57
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  好操逼在线视频 | 黄色小视频在线免费观看 | 日本亲子乱婬A片C0m | xxx.羞羞| 99热精品免费观看 |