鍍金池/ 教程/ Linux/ Tomcat 的 JDBC 連接池
連接器
JSPs
重寫機制
CGI
Tomcat Manager
Windows 認證
代理支持
虛擬主機
安全性注意事項
如何在 Maven 中使用 Tomcat 庫
安裝
MBean 描述符
JNDI 資源
類加載機制
Tomcat Web 應(yīng)用部署
基于 APR 的原生庫
負載均衡器
安全管理
附加組件
監(jiān)控與管理
Windows 服務(wù)
集群化與會話復(fù)制
高級 IO 機制
SSI(服務(wù)器端嵌入)
WebSocket 支持
JDBC 數(shù)據(jù)源
日志機制
默認 Servlet
SSL/TLS 配置
Tomcat 的 JDBC 連接池
第一個應(yīng)用
簡介
Realm 配置

Tomcat 的 JDBC 連接池

簡介

JDBC 連接池 org.apache.tomcat.jdbc.poolApache Commons DBCP 連接池的一種替換或備選方案。

那究竟為何需要一個新的連接池?

原因如下:

  1. Commons DBCP 1.x 是單線程。為了線程安全,在對象分配或?qū)ο蠓祷氐亩唐趦?nèi),Commons 鎖定了全部池。但注意這并不適用于 Commons DBCP 2.x。
  2. Commons DBCP 1.x 可能會變得很慢。當(dāng)邏輯 CPU 數(shù)目增長,或者試圖借出或歸還對象的并發(fā)線程增加時,性能就會受到影響。高并發(fā)系統(tǒng)受到的影響會更為顯著。注意這并不適用于 Commons DBCP 2.x。
  3. Commons DBCP 擁有 60 多個類。tomcat-jdbc-pool 核心只有 8 個類。因此為了未來需求變更著想,肯定需要更少的改動。我們真正需要的只是連接池本身,其余的只是附屬。
  4. Commons DBCP 使用靜態(tài)接口,因此對于指定版本的 JRE,只能采用正確版本的 DBCP,否則就會出現(xiàn) NoSuchMethodException 異常。
  5. 當(dāng)DBCP 可以用其他更簡便的實現(xiàn)來替代時,實在不值得重寫那 60 個類。
  6. Tomcat JDBC 連接池?zé)o需為庫本身添加額外線程,就能獲取異步獲取連接。
  7. Tomcat JDBC 連接池是 Tomcat 的一個模塊,依靠 Tomcat JULI 這個簡化了的日志架構(gòu)。
  8. 使用 javax.sql.PooledConnection 接口獲取底層連接。
  9. 防止饑餓。如果池變空,線程將等待一個連接。當(dāng)連接返回時,池就將喚醒正確的等待線程。大多數(shù)連接池只會一直維持饑餓狀態(tài)。

Tomcat JDBC 連接池還具有一些其他連接池實現(xiàn)所沒有的特點:

  1. 支持高并發(fā)環(huán)境與多核/CPU 系統(tǒng)。
  2. 接口的動態(tài)實現(xiàn)。支持 java.sql 與 java.sql 接口(只要 JDBC 驅(qū)動),甚至在利用低版本的 JDK 來編譯時。
  3. 驗證間隔時間。我們不必每次使用單個連接時都進行驗證,可以在借出或歸還連接時進行驗證,只要不低于我們所設(shè)定的間隔時間就行。
  4. 只執(zhí)行一次查詢。當(dāng)與數(shù)據(jù)庫建立起連接時,只執(zhí)行一次的可配置查詢。這項功能對會話設(shè)置非常有用,因為你可能會想在連接建立的整個時段內(nèi)都保持會話。
  5. 能夠配置自定義攔截器。通過自定義攔截器來增強功能??梢允褂脭r截器來采集查詢統(tǒng)計,緩存會話狀態(tài),重新連接之前失敗的連接,重新查詢,緩存查詢結(jié)果,等等。由于可以使用大量的選項,所以這種自定義攔截器也是沒有限制的,跟 java.sql/javax.sql 接口的 JDK 版本沒有任何關(guān)系。
  6. 高性能。后文將舉例展示一些性能差異。
  7. 極其簡單。它的實現(xiàn)非常簡單,代碼行數(shù)與源文件都非常少,這都有賴于從一開始研發(fā)它時,就把簡潔當(dāng)做重中之重。對比一下 c3p0 ,它的源文件超過了 200 個(最近一次統(tǒng)計),而 Tomcat JDBC 核心只有 8 個文件,連接池本身則大約只有這個數(shù)目的一半,所以能夠輕易地跟蹤和修改可能出現(xiàn)的 Bug。
  8. 異步連接獲取。可將連接請求隊列化,系統(tǒng)返回 Future<Connection>
  9. 更好地處理空閑連接。不再簡單粗暴地直接把空閑連接關(guān)閉,而是仍然把連接保留在池中,通過更為巧妙的算法控制空閑連接池的規(guī)模。
  10. 可以控制連接應(yīng)被廢棄的時間:當(dāng)池滿了即廢棄,或者指定一個池使用容差值,發(fā)生超時就進行廢棄處理。
  11. 通過查詢或語句來重置廢棄連接計時器。允許一個使用了很長時間的連接不因為超時而被廢棄。這一點是通過使用 ResetAbandonedTimer 來實現(xiàn)的。
  12. 經(jīng)過指定時間后,關(guān)閉連接。與返回池的時間相類似。
  13. 當(dāng)連接要被釋放時,獲取 JMX 通知并記錄所有日志。它類似于 removeAbandonedTimeout,但卻不需要采取任何行為,只需要報告信息即可。通過 suspectTimeout 屬性來實現(xiàn)。
  14. 可以通過 java.sql.Driver、javax.sql.DataSourcejavax.sql.XADataSource 獲取連接。通過 dataSourcedataSourceJNDI 屬性實現(xiàn)這一點。
  15. 支持 XA 連接。

使用方法

對于熟悉 Commons DBCP 的人來說,轉(zhuǎn)而使用 Tomcat 連接池是非常簡單的事。從其他連接池轉(zhuǎn)換過來也非常容易。

1. 附加功能

除了其他多數(shù)連接池能夠提供的功能外,Tomcat 連接池還提供了一些附加功能:

  • initSQL 當(dāng)連接創(chuàng)建后,能夠執(zhí)行一個 SQL 語句(只執(zhí)行一次)。
  • validationInterval 恰當(dāng)?shù)卦谶B接上運行驗證,同時又能避免太多頻繁地執(zhí)行驗證。
  • jdbcInterceptors 靈活并且可插拔的攔截器,能夠?qū)Τ剡M行各種自定義,執(zhí)行各種查詢,處理結(jié)果集。下文將予以詳述。
  • fairQueue 將 fair 標志設(shè)為 true,以達成線程公平性,或使用異步連接獲取。

2. Apache Tomcat 容器內(nèi)部

Tomcat JDBC 文檔中,Tomcat 連接池被配置為一個資源。唯一的區(qū)別在于,你必須指定 factory 屬性,并將其值設(shè)為 org.apache.tomcat.jdbc.pool.DataSourceFactory

3. 獨立性

連接池只有一個從屬文件,tomcat-juli.jar。要想在使用 bean 實例化的單一項目中使用池,實例化的 Bean 為org.apache.tomcat.jdbc.pool.DataSource。下文講到將連接池配置為 JNDI 資源時會涉及到同一屬性,也是用來將數(shù)據(jù)源配置成 bean 的。

4. JMX

連接池對象暴露了一個可以被注冊的 MBean。為了讓連接池對象創(chuàng)建 MBean,jmxEnabled 標志必須設(shè)為 true。這并不是說連接池會注冊到 MBean 服務(wù)器。在像 Tomcat 這樣的容器中,Tomcat 本身注冊就在 MBean 服務(wù)器上注冊了 DataSource。org.apache.tomcat.jdbc.pool.DataSource 對象會注冊實際的連接池 MBean。如果你在容器外運行,可以將 DataSource 注冊在任何你指定的對象名下,然后將這種注冊傳播到底層池。要想這樣做,你必須調(diào)用 mBeanServer.registerMBean(dataSource.getPool().getJmxPool(),objectname)。在調(diào)用之前,一定要保證通過調(diào)用 dataSource.createPool() 創(chuàng)建了池。

屬性

為了能夠順暢地在 Commons DBCP 與 Tomcat JDBC 連接池 之間轉(zhuǎn)換,大多數(shù)屬性名稱及其含義都是相同的。

1. JNDI 工廠與類型

屬性 描述
factory 必需的屬性,其值應(yīng)為 org.apache.tomcat.jdbc.pool.DataSourceFactory
type 類型應(yīng)為 javax.sql.DataSourcejavax.sql.XADataSource。
根據(jù)類型,將創(chuàng)建org.apache.tomcat.jdbc.pool.DataSourceorg.apache.tomcat.jdbc.pool.XADataSource。

2. 系統(tǒng)屬性

系統(tǒng)屬性作用于 JVM 范圍,影響創(chuàng)建于 JVM 內(nèi)的所有池。

屬性 描述
org.apache.tomcat.jdbc.pool.onlyAttemptCurrentClassLoader 布爾值,默認為 false??刂苿討B(tài)類(如JDBC 驅(qū)動、攔截器、驗證器)的加載。如果采用默認值,池會首先利用當(dāng)前類加載器(比如已經(jīng)加載池類的類加載器)加載類;如果類加載失敗,則嘗試利用線程上下文加載器加載。取值為 true 時,會向后兼容 Apache Tomcat 8.0.8 及更早版本,只會采用當(dāng)前類加載器。如果未設(shè)置,則取默認值。

3. 常用屬性

屬性 描述
defaultAutoCommit (布爾值)連接池所創(chuàng)建的連接默認自動提交狀態(tài)。如果未設(shè)置,則默認采用 JDBC 驅(qū)動的缺省值(如果未設(shè)置,則不會調(diào)用 setAutoCommit 方法)。
defaultReadOnly (布爾值)連接池所創(chuàng)建的連接默認只讀狀態(tài)。如果未設(shè)置,將不會調(diào)用 setReadOnly 方法。(有些驅(qū)動并不支持只讀模式,比如:informix)
defaultTransactionIsolation (字符串)連接池所創(chuàng)建的連接的默認事務(wù)隔離狀態(tài)。取值范圍為:(參考 javadoc)
  • NONE
  • READ_COMMITTED
  • READ_UNCOMMITTED
  • REPEATABLE_READ
  • SERIALIZABLE

  • 如果未設(shè)置該值,則不會調(diào)用任何方法,默認為 JDBC 驅(qū)動。
    defaultCatalog (字符串)連接池所創(chuàng)建的連接的默認catalog。
    driverClassName (字符串)所要使用的 JDBC 驅(qū)動的完全限定的 Java 類名。該驅(qū)動必須能從與 tomcat-jdbc.jar 同樣的類加載器訪問
    username (字符串)傳入 JDBC 驅(qū)動以便建立連接的連接用戶名。注意,DataSource.getConnection(username,password) 方法默認不會使用傳入該方法內(nèi)的憑證,但會使用這里的配置信息。可參看 alternateUsernameAllowed 了解更多詳情。
    password (字符串)傳入 JDBC 驅(qū)動以便建立連接的連接密碼。注意,DataSource.getConnection(username,password) 方法默認不會使用傳入該方法內(nèi)的憑證,但會使用這里的配置信息。可參看 alternateUsernameAllowed 了解更多詳情。
    maxActive (整形值)池同時能分配的活躍連接的最大數(shù)目。默認為 100。
    maxIdle (整型值)池始終都應(yīng)保留的連接的最大數(shù)目。默認為 maxActive:100。會周期性檢查空閑連接(如果啟用該功能),留滯時間超過 minEvictableIdleTimeMillis 的空閑連接將會被釋放。(請參考 testWhileIdle
    minIdle (整型值)池始終都應(yīng)保留的連接的最小數(shù)目。如果驗證查詢失敗,則連接池會縮減該值。默認值取自 initialSize:10(請參考 testWhileIdle)。
    initialSize (整型值)連接器啟動時創(chuàng)建的初始連接數(shù)。默認為 10。
    maxWait (整型值)在拋出異常之前,連接池等待(沒有可用連接時)返回連接的最長時間,以毫秒計。默認為 30000(30 秒)
    testOnBorrow (布爾值)默認值為 false。從池中借出對象之前,是否對其進行驗證。如果對象驗證失敗,將其從池中清除,再接著去借下一個。注意:為了讓 true 值生效,validationQuery 參數(shù)必須為非空字符串。為了實現(xiàn)更高效的驗證,可以采用 validationInterval。
    testOnReturn (布爾值)默認值為 false。將對象返回池之前,是否對齊進行驗證。注意:為了讓 true 值生效,validationQuery 參數(shù)必須為非空字符串。
    testWhileIdle (布爾值)是否通過空閑對象清除者(如果存在的話)驗證對象。如果對象驗證失敗,則將其從池中清除。注意:為了讓 true 值生效,validationQuery 參數(shù)必須為非空字符串。該屬性默認值為 false,為了運行池的清除/測試線程,必須設(shè)置該值。(另請參閱 timeBetweenEvictionRunsMillis
    validationQuery (字符串)在將池中連接返回給調(diào)用者之前,用于驗證這些連接的 SQL 查詢。如果指定該值,則該查詢不必返回任何數(shù)據(jù),只是不拋出 SQLException 異常。默認為 null。實例值為:SELECT 1(MySQL) select 1 from dual(Oracle) SELECT 1(MySQL Server)。
    validationQueryTimeout (整型值)連接驗證失敗前的超時時間(以秒計)。通過在執(zhí)行 validationQuery 的語句上調(diào)用 java.sql.Statement.setQueryTimeout(seconds) 來實現(xiàn)。池本身并不會讓查詢超時,完全是由 JDBC 來強制實現(xiàn)。若該值小于或等于 0,則禁用該功能。默認為 -1
    validatorClassName (字符串)實現(xiàn) org.apache.tomcat.jdbc.pool.Validator 接口并提供了一個無參(可能是隱式的)構(gòu)造函數(shù)的類名。如果指定該值,將通過該類來創(chuàng)建一個 Validator 實例來驗證連接,代替任何驗證查詢。默認為 null,范例值為:com.mycompany.project.SimpleValidator。
    timeBetweenEvictionRunsMillis (整型值)空閑連接驗證/清除線程運行之間的休眠時間(以毫秒計)。不能低于 1 秒。該值決定了我們檢查空閑連接、廢棄連接的頻率,以及驗證空閑連接的頻率。默認為 5000(5 秒)
    numTestsPerEvictionRun (整型值)Tomcat JDBC 連接池沒有用到這個屬性。
    minEvictableIdleTimeMillis (整型值)在被確定應(yīng)被清除之前,對象在池中保持空閑狀態(tài)的最短時間(以毫秒計)。默認為 60000(60 秒)
    accessToUnderlyingConnectionAllowed (布爾值)沒有用到的屬性??梢栽跉w入池內(nèi)的連接上調(diào)用 unwrap來訪問。參閱 javax.sql.DataSource 接口的相關(guān)介紹,或者通過反射調(diào)用 getConnection,或者將對象映射為 javax.sql.PooledConnection。
    removeAbandoned (布爾值)該值為標志(Flag)值,表示如果連接時間超出了 removeAbandonedTimeout,則將清除廢棄連接。如果該值被設(shè)置為 true,則如果連接時間大于 removeAbandonedTimeout,該連接會被認為是廢棄連接,應(yīng)予以清除。若應(yīng)用關(guān)閉連接失敗時,將該值設(shè)為 true 能夠恢復(fù)該應(yīng)用的數(shù)據(jù)庫連接。另請參閱 logAbandoned。默認值為 false
    removeAbandonedTimeout (整型值)在廢棄連接(仍在使用)可以被清除之前的超時秒數(shù)。默認為 60(60 秒)。應(yīng)把該值設(shè)定為應(yīng)用可能具有的運行時間最長的查詢。
    logAbandoned (布爾值)標志能夠針對丟棄連接的應(yīng)用代碼,進行堆棧跟蹤記錄。由于生成堆棧跟蹤,對廢棄連接的日志記錄會增加每一個借取連接的開銷。默認為 false
    connectionProperties (字符串)在建立新連接時,發(fā)送給 JDBC 驅(qū)動的連接屬性。字符串格式必須為:[propertyName=property;]*。注意:user 與 password 屬性會顯式傳入,因此這里并不需要包括它們。默認為 null。
    poolPreparedStatements (布爾值)未使用的屬性
    maxOpenPreparedStatements (整型值)未使用的屬性

    4. Tomcat JDBC 增強屬性

    屬性 描述
    initSQL 字符串值。當(dāng)連接第一次創(chuàng)建時,運行的自定義查詢。默認值為 null。
    jdbcInterceptors 字符串。繼承自類 org.apache.tomcat.jdbc.pool.JdbcInterceptor 的子類類名列表,由分號分隔。關(guān)于格式及范例,可參見下文的配置 JDBC 攔截器。

    這些攔截器將會插入到 java.sql.Connection 對象的操作隊列中。

    預(yù)定義的攔截器有:
  • org.apache.tomcat.jdbc.pool.interceptor
  • ConnectionState——記錄自動提交、只讀、catalog以及事務(wù)隔離級別等狀態(tài)。
  • org.apache.tomcat.jdbc.pool.interceptor
  • StatementFinalizer——記錄打開的語句,并當(dāng)連接返回池后關(guān)閉它們。


  • 有關(guān)更多預(yù)定義攔截器的詳盡描述,可參閱JDBC 攔截器
    validationInterval 長整型值。為避免過度驗證而設(shè)定的頻率時間值(以秒計)。最多以這種頻率運行驗證。如果連接應(yīng)該進行驗證,但卻沒能在此間隔時間內(nèi)得到驗證,則會重新對其進行驗證。默認為 30000(30 秒)。
    jmxEnabled 布爾值。是否利用 JMX 注冊連接池。默認為 true。
    fairQueue 布爾值。假如想用真正的 FIFO 方式公平對待 getConnection 調(diào)用,則取值為 true。對空閑連接列表將采用 org.apache.tomcat.jdbc.pool.FairBlockingQueue 實現(xiàn)。默認值為 true。如果想使用異步連接獲取功能,則必須使用該標志。
    設(shè)置該標志可保證線程能夠按照連接抵達順序來接收連接。
    在性能測試時,鎖及鎖等待的實現(xiàn)方式有很大差異。當(dāng) fairQueue=true 時,根據(jù)所運行的操作系統(tǒng),存在一個決策過程。假如系統(tǒng)運行在 Linux 操作系統(tǒng)(屬性 os.name = linux)上,為了禁止這個 Linux 專有行為,但仍想使用公平隊列,那么只需在連接池類加載之前,將 org.apache.tomcat.jdbc.pool.FairBlockingQueue.ignoreOS=true 添加到系統(tǒng)屬性上。
    abandonWhenPercentageFull 整型值。除非使用中連接的數(shù)目超過 abandonWhenPercentageFull 中定義的百分比,否則不會關(guān)閉并報告已廢棄的連接(因為超時)。取值范圍為 0-100。默認值為 0,意味著只要達到 removeAbandonedTimeout,就應(yīng)關(guān)閉連接。
    maxAge 長整型值。連接保持時間(以毫秒計)。當(dāng)連接要返回池中時,連接池會檢查是否達到 now - time-when-connected > maxAge 的條件,如果條件達成,則關(guān)閉該連接,不再將其返回池中。默認值為 0,意味著連接將保持開放狀態(tài),在將連接返回池中時,不會執(zhí)行任何年齡檢查。
    useEquals 布爾值。如果想讓 ProxyConnection 類使用 String.equals,則將該值設(shè)為 true;若想在對比方法名稱時使用 ==,則應(yīng)將其設(shè)為 false。該屬性不能用于任何已添加的攔截器中,因為那些攔截器都是分別配置的。默認值為 true。
    suspectTimeout 整型值。超時時間(以秒計)。默認值為 0
    類似于 removeAbandonedTimeout,但不會把連接當(dāng)做廢棄連接從而有可能關(guān)閉連接。如果 logAbandoned 設(shè)為 true,它只會記錄下警告。如果該值小于或等于 0,則不會執(zhí)行任何懷疑式檢查。如果超時值大于 0,而連接還沒有被廢棄,或者廢棄檢查被禁用時,才會執(zhí)行懷疑式檢查。如果某個連接被懷疑到,則記錄下 WARN 信息并發(fā)送一個 JMX 通知。
    rollbackOnReturn 布爾值。如果 autoCommit==false,那么當(dāng)連接返回池中時,池會在連接上調(diào)用回滾方法,從而終止事務(wù)。默認值為 false。
    commitOnReturn 布爾值。如果 autoCommit==false,那么當(dāng)連接返回池中時,池會在連接上調(diào)用提交方法,從而完成事務(wù);如果 rollbackOnReturn==true,則忽略該屬性。默認值為 false。
    alternateUsernameAllowed 布爾值。出于性能考慮,JDBC 連接池默認會忽略 DataSource.getConnection(username,password)調(diào)用,只返回之前池化的具有全局配置屬性 usernamepassword的連接。

    但經(jīng)過配置,連接池還可以允許使用不同的憑證來請求每一個連接。為了啟用這項在DataSource.getConnection(username,password)調(diào)用中描述的功能,只需將 alternateUsernameAllowed 設(shè)為 true。
    如果你請求一個連接,憑證為 user 1/password 1,而連接之前使用的是 user 2/password 2 憑證,那么連接將被關(guān)閉,重新利用請求的憑證來開啟。按照這種方式,池的容量始終以全局級別管理,而不是限于模式(schema)級別。
    默認值為 false
    該屬性作為一個改進方案,被添加到了 bug 50025 中。
    dataSource (javax.sql.DataSource)將數(shù)據(jù)源注入連接池,從而使池利用數(shù)據(jù)源來獲取連接,而不是利用 java.sql.Driver 接口來建立連接。它非常適于使用數(shù)據(jù)源(而非連接字符串)來池化 XA 連接或者已建立的連接時。默認值為 null
    dataSourceJNDI 字符串。在 JNDI 中查找的數(shù)據(jù)源的 JNDI 名稱,隨后將用于建立數(shù)據(jù)庫連接。參看 datasource 屬性的介紹。默認值為 null
    useDisposableConnectionFacade 布爾值。如果希望在連接上放上一個門面對象,從而使連接在關(guān)閉后無法重用,則要將值設(shè)為 true。這能防止線程繼續(xù)引用一個已被關(guān)閉的連接,并繼續(xù)在連接上查詢。默認值為 true。
    logValidationErrors 布爾值。設(shè)為 true 時,能將驗證階段的錯誤記錄到日志文件中,錯誤會被記錄為 SEVERE??紤]到了向后兼容性,默認值為 false
    propagateInterruptState 布爾值。傳播已中斷的線程(還沒有清除中斷狀態(tài))的中斷狀態(tài)??紤]到了向后兼容性,默認值為 false。
    ignoreExceptionOnPreLoad 布爾值。在初始化池時,是否忽略連接創(chuàng)建錯誤。取值為 true時表示忽略;設(shè)為 false 時,拋出異常,從而宣告池初始化失敗。默認值為 false。

    高級用法

    1. JDBC 攔截器

    要想看看攔截器使用方法的具體范例,可以看看 org.apache.tomcat.jdbc.pool.interceptor.ConnectionState。這個簡單的攔截器緩存了三個屬性:autoCommit、readOnlytransactionIsolation,為的是避免系統(tǒng)與數(shù)據(jù)庫之間無用的往返。

    當(dāng)需求增加時,姜維連接池核心增加更多的攔截器。歡迎貢獻你的才智!

    攔截器當(dāng)然并不局限于 java.sql.Connection,當(dāng)然也可以對方法調(diào)用的任何結(jié)果進行包裝。你可以構(gòu)建查詢性能分析器,以便當(dāng)查詢運行時間超過預(yù)期時間時提供 JMX 通知。

    2. 配置 JDBC 攔截器

    JDBC 攔截器是通過 jdbcInterceptor 屬性來配置的。該屬性值包含一列由分號分隔的類名。如果這些類名非完全限定,就會在它們的前面加上 org.apache.tomcat.jdbc.pool.interceptor. 前綴。

    范例:
    jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState; org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
    它實際上等同于:
    jdbcInterceptors="ConnectionState;StatementFinalizer"

    攔截器也同樣有屬性。攔截器的屬性指定在類名后的括號里,如果設(shè)置多個屬性,則用逗號分隔開。

    范例:

    jdbcInterceptors="ConnectionState;StatementFinalizer(useEquals=true)"

    系統(tǒng)會自動忽略屬性名稱、屬性值以及類名前后多余的空格字符。

    org.apache.tomcat.jdbc.pool.JdbcInterceptor

    所有攔截器的抽象基類,無法實例化。

    屬性 描述
    useEquals (布爾值)如果希望 ProxyConnection 類使用 String.equals,則設(shè)為 true;當(dāng)希望在對比方法名時使用 ==,則設(shè)為 false。默認為 true。

    org.apache.tomcat.jdbc.pool.interceptor.ConnectionState

    它能為下列屬性緩存連接:autoCommitreadOnly、transactionIsolationcatalog。這是一種性能增強功能,當(dāng)利用已設(shè)定的值來調(diào)用 getter 與 setter 時,它能夠避免往返數(shù)據(jù)庫。

    org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer

    跟蹤所有使用 createStatement、prepareStatementprepareCall 的語句,當(dāng)連接返回池后,關(guān)閉這些語句。

    屬性 描述
    trace (以字符串形式表示的布爾值)對未關(guān)閉語句進行跟蹤。當(dāng)啟用跟蹤且連接被關(guān)閉時,如果相關(guān)語句沒有關(guān)閉,則攔截器會記錄所有的堆棧跟蹤。默認值為 false。

    org.apache.tomcat.jdbc.pool.interceptor.StatementCache

    緩存連接中的 PreparedStatementCallableStatement 實例。

    它會針對每個連接對這些語句進行緩存,然后計算池中所有連接的整體緩存數(shù),如果緩存數(shù)超過了限制 max,就不再對隨后的語句進行緩存,而是直接關(guān)閉它們。

    屬性 描述
    prepared (以字符串形式表示的布爾值)對使用 prepareStatement 調(diào)用創(chuàng)建的 PreparedStatement 實例進行緩存。默認為 true
    callable (以字符串形式表示的布爾值)對使用 prepareCall 調(diào)用創(chuàng)建的 CallableStatement 實例進行緩存。默認為 false
    max (以字符串形式表示的整型值)連接池中的緩存語句的數(shù)量限制。默認為 50

    org.apache.tomcat.jdbc.pool.interceptor.StatementDecoratorInterceptor

    請參看 48392。攔截器會包裝語句和結(jié)果集,從而防止對使用了 ResultSet.getStatement().getConnection()Statement.getConnection() 方法的實際連接進行訪問。

    org.apache.tomcat.jdbc.pool.interceptor.QueryTimeoutInterceptor

    當(dāng)新語句創(chuàng)建時,自動調(diào)用 java.sql.Statement.setQueryTimeout(seconds)。池本身并不會讓查詢超時,完全是依靠 JDBC 驅(qū)動來強制查詢超時。

    屬性 描述
    queryTimeout (以字符串形式表示的整型值)查詢超時的毫秒數(shù)。默認為 1000 毫秒。

    org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport

    當(dāng)查詢超過失敗容差值時,記錄查詢性能并發(fā)布日志項目。使用的日志級別為 WARN。

    屬性 描述
    threshold (以字符串形式表示的整型值)查詢應(yīng)超時多少毫秒才發(fā)布日志警告。默認為 1000 毫秒
    maxQueries (以字符串形式表示的整型值)為保留內(nèi)存空間,所能記錄的最大查詢數(shù)量。默認為 1000
    logSlow (以字符串形式表示的布爾值)如果想記錄較慢的查詢,設(shè)為 true。默認為 true
    logFailed (以字符串形式表示的布爾值)如果想記錄失敗查詢,設(shè)為 true。默認為 true

    org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx

    這是對 SlowQueryReport 的擴展,除了發(fā)布日志項目外,它還發(fā)布 JMX 通知,以便監(jiān)視工具作出相關(guān)反應(yīng)。該類從其父類繼承了所有屬性。它使用了 Tomcat 的 JMX 引擎,所以在 Tomcat 容器外部是無效的。使用該類時,默認情況下,是通過 ConnectionPool MBean 來發(fā)送 JMX 通知。如果 notifyPool=false,則 SlowQueryReportJmx 也可以注冊一個 MBean。

    屬性 描述
    notifyPool (以字符串形式表示的布爾值)如果希望用 SlowQueryReportJmx MBean 發(fā)送 JMX 通知,則設(shè)為 false。默認為 true
    objectName 字符串。定義一個有效的 javax.management.ObjectName 字符串,用于將這一對象注冊到平臺所用的 mbean 服務(wù)器上。默認值為 null??梢允褂?tomcat.jdbc:type=org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx,name=the-name-of-the-pool 來注冊對象。

    org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer

    當(dāng)連接簽出池中后,廢棄計時器即開始計時。這意味著如果超時為 30 秒,而你使用連接運行了 10 個 10秒的查詢,那么它就會被標為廢棄,并可能依靠 abandonWhenPercentageFull 屬性重新聲明。每次成功地在連接上執(zhí)行操作或執(zhí)行查詢時,該攔截器就會重設(shè)簽出計時器。

    代碼范例

    其他 JDBC 用途的 Tomcat 配置范例可以參考 相關(guān)的 Tomcat 文檔。

    簡單的 Java

    下面這個簡單的范例展示了如何創(chuàng)建并使用數(shù)據(jù)源:

      import java.sql.Connection;
      import java.sql.ResultSet;
      import java.sql.Statement;
    
      import org.apache.tomcat.jdbc.pool.DataSource;
      import org.apache.tomcat.jdbc.pool.PoolProperties;
    
      public class SimplePOJOExample {
    
          public static void main(String[] args) throws Exception {
              PoolProperties p = new PoolProperties();
              p.setUrl("jdbc:mysql://localhost:3306/mysql");
              p.setDriverClassName("com.mysql.jdbc.Driver");
              p.setUsername("root");
              p.setPassword("password");
              p.setJmxEnabled(true);
              p.setTestWhileIdle(false);
              p.setTestOnBorrow(true);
              p.setValidationQuery("SELECT 1");
              p.setTestOnReturn(false);
              p.setValidationInterval(30000);
              p.setTimeBetweenEvictionRunsMillis(30000);
              p.setMaxActive(100);
              p.setInitialSize(10);
              p.setMaxWait(10000);
              p.setRemoveAbandonedTimeout(60);
              p.setMinEvictableIdleTimeMillis(30000);
              p.setMinIdle(10);
              p.setLogAbandoned(true);
              p.setRemoveAbandoned(true);
              p.setJdbcInterceptors(
                "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+
                "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
              DataSource datasource = new DataSource();
              datasource.setPoolProperties(p);
    
              Connection con = null;
              try {
                con = datasource.getConnection();
                Statement st = con.createStatement();
                ResultSet rs = st.executeQuery("select * from user");
                int cnt = 1;
                while (rs.next()) {
                    System.out.println((cnt++)+". Host:" +rs.getString("Host")+
                      " User:"+rs.getString("User")+" Password:"+rs.getString("Password"));
                }
                rs.close();
                st.close();
              } finally {
                if (con!=null) try {con.close();}catch (Exception ignore) {}
              }
          }
    
      }
    

    作為資源使用

    下例展示了如何為 JNDI 查找配置資源。

    <Resource name="jdbc/TestDB"
              auth="Container"
              type="javax.sql.DataSource"
              factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
              testWhileIdle="true"
              testOnBorrow="true"
              testOnReturn="false"
              validationQuery="SELECT 1"
              validationInterval="30000"
              timeBetweenEvictionRunsMillis="30000"
              maxActive="100"
              minIdle="10"
              maxWait="10000"
              initialSize="10"
              removeAbandonedTimeout="60"
              removeAbandoned="true"
              logAbandoned="true"
              minEvictableIdleTimeMillis="30000"
              jmxEnabled="true"
              jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
                org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
              username="root"
              password="password"
              driverClassName="com.mysql.jdbc.Driver"
              url="jdbc:mysql://localhost:3306/mysql"/>
    

    異步連接獲取

    Tomcat JDBC 連接池支持異步連接獲取,無需為池庫添加任何額外線程。這是通過在數(shù)據(jù)源上添加一個方法 Future<Connection> getConnectionAsync() 來實現(xiàn)的。為了使用異步獲取,必須滿足兩個條件:

    1. 必須把 failQueue 屬性設(shè)為 true
    2. 必須把數(shù)據(jù)源轉(zhuǎn)換為 org.apache.tomcat.jdbc.pool.DataSource。

    下例就使用了異步獲取功能:

      Connection con = null;
      try {
        Future<Connection> future = datasource.getConnectionAsync();
        while (!future.isDone()) {
          System.out.println("Connection is not yet available. Do some background work");
          try {
            Thread.sleep(100); //simulate work
          }catch (InterruptedException x) {
            Thread.currentThread().interrupt();
          }
        }
        con = future.get(); //should return instantly
        Statement st = con.createStatement();
        ResultSet rs = st.executeQuery("select * from user");
    

    攔截器

    對于啟用、禁止或修改特定連接或其組件的功能而言,使用攔截器無疑是一種非常強大的方式。There are many different use cases for when interceptors are useful。默認情況下,基于性能方面的考慮,連接池是無狀態(tài)的。連接池本身所插入的狀態(tài)是 defaultAutoCommitdefaultReadOnly、defaultTransactionIsolation,或 defaultCatalog(如果設(shè)置了這些狀態(tài))。這 4 個狀態(tài)只有在連接創(chuàng)建時才設(shè)置。無論這些屬性是否在連接使用期間被修改,池本身都不能重置它們。

    攔截器必須擴展自 org.apache.tomcat.jdbc.pool.JdbcInterceptor 類。該類相當(dāng)簡單,你必須利用一個無參數(shù)構(gòu)造函數(shù)。

      public JdbcInterceptor() {
      }  

    當(dāng)從連接池借出一個連接時,攔截器能夠通過實現(xiàn)以下方法,初始化這一事件或以一些其他形式來響應(yīng)該事件。

    public abstract void reset(ConnectionPool parent, PooledConnection con);

    上面這個方法有兩個參數(shù),一個是連接池本身的引用 ConnectionPool parent,一個是底層連接的引用 PooledConnection con

    當(dāng)調(diào)用 java.sql.Connection 對象上的方法時,會導(dǎo)致以下方法被調(diào)用:

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable

    Method method 是被調(diào)用的實際方法,Object[] args 是參數(shù)。通過觀察下面這個非常簡單的例子,我們可以解釋如果當(dāng)連接已經(jīng)關(guān)閉時,如何讓 java.sql.Connection.close() 的調(diào)用變得無用。

      if (CLOSE_VAL==method.getName()) {
          if (isClosed()) return null; //noop for already closed.
      }
      return super.invoke(proxy,method,args);  
    

    池啟動與停止

    當(dāng)連接池開啟或關(guān)閉時,你可以得到相關(guān)通知??赡苊總€攔截器類只通知一次,即使它是一個實例方法。也可能使用當(dāng)前未連接到池中的攔截器來通知你。

      public void poolStarted(ConnectionPool pool) {
      }
    
      public void poolClosed(ConnectionPool pool) {
      }
    

    當(dāng)重寫這些方法時,如果你擴展自 JdbcInterceptor 之外的類,不要忘記調(diào)用超類。

    配置攔截器

    攔截器可以通過 jdbcInterceptors 屬性或 setJdbcInterceptors 方法來配置。攔截器也可以有屬性,可以通過如下方式來配置:

      String jdbcInterceptors=
        "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState(useEquals=true,fast=yes)"
    

    攔截器屬性

    既然攔截器也有屬性,那么你也可以讀取其中的屬性值。你可以重寫 setProperties 方法。

      public void setProperties(Map<String, InterceptorProperty> properties) {
         super.setProperties(properties);
         final String myprop = "myprop";
         InterceptorProperty p1 = properties.get(myprop);
         if (p1!=null) {
             setMyprop(Long.parseLong(p1.getValue()));
         }
      }
    

    獲取實際的 JDBC 連接

    連接池圍繞實際的連接創(chuàng)建包裝器,為的是能夠正確地池化。同樣,為了執(zhí)行特定的功能,我們也可以在這些包裝器中創(chuàng)建攔截器。如果不需要獲取實際的連接,可以使用 javax.sql.PooledConnection 接口。

      Connection con = datasource.getConnection();
      Connection actual = ((javax.sql.PooledConnection)con).getConnection();
    

    構(gòu)建

    下面利用 1.6 來構(gòu)建 JDBC 連接池代碼,但它也可以向后兼容到 1.5 運行時環(huán)境。為了單元測試,使用 1.6 或更高版本。

    更多的關(guān)于 JDBC 用途的 Tomcat 配置范例可參看 [Tomcat 文檔]()。

    從源代碼構(gòu)建

    構(gòu)建非常簡單。池依賴于 tomcat-juli.jar,在這種情況下,需要 SlowQueryReportJmx。

      javac -classpath tomcat-juli.jar \
            -d . \
            org/apache/tomcat/jdbc/pool/*.java \
            org/apache/tomcat/jdbc/pool/interceptor/*.java \
            org/apache/tomcat/jdbc/pool/jmx/*.java
    

    構(gòu)建文件位于 Tomcat 的源代碼倉庫中。

    為了方便起見,在通過簡單構(gòu)建命令生成所需文件的地方也包含了一個構(gòu)建文件。

      ant download  (downloads dependencies)
      ant build     (compiles and generates .jar files)
      ant dist      (creates a release package)
      ant test      (runs tests, expects a test database to be setup)
    

    系統(tǒng)針對 Maven 構(gòu)建進行組織,但是沒有生成發(fā)布組件,只有庫本身。