- 簡介
- 如何使用
- 屬性
- 進階用法
- JDBC 攔截器
- 組態 JDBC 攔截器
- org.apache.tomcat.jdbc.pool.JdbcInterceptor
- org.apache.tomcat.jdbc.pool.interceptor.ConnectionState
- org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer
- org.apache.tomcat.jdbc.pool.interceptor.StatementCache
- org.apache.tomcat.jdbc.pool.interceptor.StatementDecoratorInterceptor
- org.apache.tomcat.jdbc.pool.interceptor.QueryTimeoutInterceptor
- org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport
- org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx
- org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer
- 程式碼範例
- 建置
Tomcat JDBC 連線池
目錄
簡介
JDBC 連線池 org.apache.tomcat.jdbc.pool
是 Apache Commons DBCP 連線池的替代品或另一種選擇。
那麼,為什麼我們需要一個新的連線池?
以下是幾個原因
- Commons DBCP 1.x 是單執行緒的。為了執行緒安全,Commons 會在物件配置和物件傳回期間短暫鎖定整個池。請注意,這不適用於 Commons DBCP 2.x。
- Commons DBCP 1.x 可能很慢。隨著邏輯 CPU 數量的增加,以及嘗試借用或傳回物件的同時執行緒數量的增加,效能會下降。對於高度並行的系統,影響可能是顯著的。請注意,這不適用於 Commons DBCP 2.x。
- Commons DBCP 有超過 60 個類別。tomcat-jdbc-pool 核心有 8 個類別,因此未來需求的修改將需要更少的變更。這正是您執行連線池本身所需的,其餘的都是額外的功能。
- Commons DBCP 使用靜態介面。這表示您必須針對特定 JRE 版本使用正確的版本,否則您可能會看到
NoSuchMethodException
例外狀況。 - 當可以用更簡單的實作完成連線池時,重寫超過 60 個類別是不值得的。
- Tomcat jdbc 池實作了非同步擷取連線的能力,而不會為函式庫本身新增額外的執行緒。
- Tomcat jdbc 池是 Tomcat 模組,它依賴 Tomcat JULI,這是 Tomcat 中使用的簡化記錄架構。
- 使用
javax.sql.PooledConnection
介面擷取基礎連線。 - 防飢餓。如果池是空的,而且執行緒正在等待連線,則當連線傳回時,池會喚醒正在等待的正確執行緒。大多數池只會挨餓。
新增其他連線池實作的功能
- 支援高度並行的環境和多核心/CPU 系統。
- 介面的動態實作,將支援您的執行時間環境的
java.sql
和javax.sql
介面(只要您的 JDBC 驅動程式也這麼做),即使使用較低版本的 JDK 編譯也是如此。 - 驗證間隔 - 我們不必每次使用連線時都進行驗證,我們可以在借用或傳回連線時執行此操作,只是不能比我們可以設定的間隔更頻繁。
- 執行一次查詢,一個可設定的查詢,只會在與資料庫建立連線時執行一次。非常適合設定您希望在連線建立期間存在的會話設定。
- 能夠設定自訂攔截器。這讓您可以撰寫自訂攔截器來增強功能。您可以使用攔截器來收集查詢統計資料、快取階段狀態、在失敗時重新連線、重試查詢、快取查詢結果等等。您的選項是無止盡的,而且攔截器是動態的,不受
java.sql
/javax.sql
介面的 JDK 版本約束。 - 高性能 - 稍後我們會展示一些效能差異
- 極為簡單,由於簡化的實作,程式碼行數和原始檔數量非常少,與有超過 200 個原始檔的 c3p0 相比(上次我們檢查時),Tomcat jdbc 的核心有 8 個檔案,連線池本身約為其一半。如果發生錯誤,它們會更快被追蹤,也更容易修復。從一開始,複雜度降低就是重點。
- 非同步連線擷取 - 您可以將連線要求排隊,並收到
Future<Connection>
回傳。 - 更好的閒置連線處理。它不會直接關閉連線,而是可以繼續集中連線,並使用更聰明的演算法調整閒置池的大小。
- 您可以決定在什麼時候將連線視為已放棄,是在池滿時,還是直接在逾時時,方法是指定池使用率閾值。
- 放棄連線計時器會在陳述式/查詢活動時重設。允許長時間使用的連線不會逾時。這是使用
ResetAbandonedTimer
達成的 - 在連線連接一段時間後關閉連線。基於年齡在回傳到池時關閉。
- 當懷疑連線已放棄時,取得 JMX 通知和日誌記錄。這類似於
removeAbandonedTimeout
,但它不採取任何動作,只報告資訊。這是使用suspectTimeout
屬性達成的。 - 連線可以從
java.sql.Driver
、javax.sql.DataSource
或javax.sql.XADataSource
擷取。這是使用dataSource
和dataSourceJNDI
屬性達成的。 - XA 連線支援
如何使用
Tomcat 連線池的使用已盡可能簡化,對於熟悉 commons-dbcp 的人來說,轉換會非常簡單。從其他連線池轉移也相當容易。
其他功能
Tomcat 連線池提供了一些其他功能,超越了大多數其他池讓您執行的功能
initSQL
- 在建立連線時執行 SQL 陳述式一次validationInterval
- 除了對連線執行驗證之外,避免過於頻繁地執行驗證。jdbcInterceptors
- 彈性且可插入的攔截器,用於建立池、查詢執行和結果集處理周圍的任何自訂功能。進階部分有更詳細的說明。fairQueue
- 將 fair 旗標設定為 true 以達成執行緒公平性或使用非同步連線擷取
在 Apache Tomcat 容器中
Tomcat 連線池設定為 Tomcat JDBC 文件 中所述的資源,唯一的差異在於您必須指定 factory
屬性,並將值設定為 org.apache.tomcat.jdbc.pool.DataSourceFactory
獨立
連線池只有一個額外的相依性,即 tomcat-juli.jar。若要使用 bean 實例化在獨立專案中設定池,則要實例化的 bean 為 org.apache.tomcat.jdbc.pool.DataSource
。用於設定連線池為 JNDI 資源的相同屬性 (如下所述),也用於設定資料來源為 bean。
JMX
連線池物件公開一個可註冊的 MBean。為了讓連線池物件建立 MBean,必須將 jmxEnabled
旗標設定為 true。這並不表示池會註冊到 MBean 伺服器,僅表示 MBean 已建立。在 Tomcat 等容器中,Tomcat 本身會將 DataSource 註冊到 MBean 伺服器,然後 org.apache.tomcat.jdbc.pool.DataSource
物件會註冊實際連線池 MBean。如果您在容器外執行,您可以自行註冊 DataSource,並在您指定的任何物件名稱下註冊,它會將註冊傳播到底層池。為此,您會呼叫 mBeanServer.registerMBean(dataSource.getPool().getJmxPool(),objectname)
。在此呼叫之前,請確保已透過呼叫 dataSource.createPool()
建立池。
屬性
為了提供從 commons-dbcp 和 tomcat-jdbc-pool 切換的簡易方式,大多數屬性都是相同的,且具有相同的意義。
JNDI 工廠和類型
屬性 | 說明 |
---|---|
factory |
需要 factory,且值應為 |
type |
類型應始終為 根據類型,將建立 |
系統屬性
系統屬性適用於整個 JVM,會影響在 JVM 中建立的所有池
屬性 | 說明 |
---|---|
org.apache.tomcat.jdbc.pool.onlyAttemptCurrentClassLoader |
(布林值) 控制動態類別 (例如 JDBC 驅動程式、攔截器和驗證器) 的類別載入。如果設定為 |
一般屬性
這些屬性由 commons-dbcp 和 tomcat-jdbc-pool 共享,在某些情況下,預設值不同。
屬性 | 說明 |
---|---|
defaultAutoCommit |
(布林值) 此池建立的連線的預設自動提交狀態。如果未設定,預設值為 JDBC 驅動程式預設值(如果未設定,則不會呼叫 |
defaultReadOnly |
(布林值) 此池建立的連線的預設唯讀狀態。如果未設定,則不會呼叫 |
defaultTransactionIsolation |
(字串) 此池建立的連線的預設 TransactionIsolation 狀態。下列其中之一:(請參閱 javadoc )
如果未設定,則不會呼叫此方法,且預設值為 JDBC 驅動程式。 |
defaultCatalog |
(字串) 此池建立的連線的預設目錄。 |
driverClassName |
(字串) 要使用的 JDBC 驅動程式的完全限定 Java 類別名稱。驅動程式必須可從與 tomcat-jdbc.jar 相同的類別載入器存取 |
username |
(字串) 要傳遞給我們的 JDBC 驅動程式以建立連線的連線使用者名稱。請注意,方法 |
password |
(字串) 要傳遞給我們的 JDBC 驅動程式以建立連線的連線密碼。請注意,方法 |
maxActive |
(整數) 同時可以從此池分配的最大主動連線數。預設值為 |
maxIdle |
(整數) 隨時應保留在池中的最大連線數。預設值為 |
minIdle |
(整數) 隨時應保留在池中的已建立連線的最小數。如果驗證查詢失敗,連線池可能會縮減到低於此數。預設值來自 |
initialSize |
(整數) 池啟動時建立的連線的初始數。預設值為 |
maxWait |
(整數) 池會等待(在沒有可用連線時)連線返回的毫秒數上限,然後擲回例外。預設值為 |
testOnBorrow |
(布林值) 指示在從池中借用物件之前是否會驗證物件。如果物件驗證失敗,將會從池中移除,而且我們會嘗試借用另一個物件。如需更有效率的驗證,請參閱 |
testOnConnect |
(布林值) 指示在第一次建立連線時是否會驗證物件。如果物件驗證失敗,將會擲回 |
testOnReturn |
(布林值) 指示在將物件傳回池中之前是否會驗證物件。預設值為 |
testWhileIdle |
(布林值) 指示物件是否會由閒置物件逐出器(如果有)驗證。如果物件驗證失敗,將會從池中移除。預設值為 |
validationQuery |
(字串) 在將連線從此池傳回呼叫端之前,將用於驗證連線的 SQL 查詢。如果已指定,此查詢不一定要傳回任何資料,但不能擲回 |
validationQueryTimeout |
(整數) 連線驗證查詢失敗之前的逾時時間(以秒為單位)。此動作會在執行 |
validatorClassName |
(字串) 實作 |
timeBetweenEvictionRunsMillis |
(int) 空閒連線驗證/清理執行緒執行間隔的毫秒數。此值不應設定在 1 秒以下。它決定我們檢查閒置、放棄的連線的頻率,以及驗證閒置連線的頻率。如果後者非零且較低,此值將被 |
numTestsPerEvictionRun |
(int) tomcat-jdbc-pool 中未使用的屬性。 |
minEvictableIdleTimeMillis |
(int) 物件在池中閒置的最小時間,然後才有資格被逐出。預設值為 |
accessToUnderlyingConnectionAllowed |
(boolean) 未使用的屬性。可透過呼叫池化連線上的 |
removeAbandoned |
(boolean) 如果放棄的連線超過 |
removeAbandonedTimeout |
(int) 在放棄(使用中)的連線可以被移除前的逾時時間(秒)。預設值為 |
logAbandoned |
(boolean) 記錄放棄連線的應用程式程式碼的堆疊追蹤的旗標。放棄的連線記錄會為每個連線借用增加負擔,因為必須產生堆疊追蹤。預設值為 |
connectionProperties |
(String) 建立新連線時將傳送到 JDBC 驅動程式的連線屬性。字串格式必須為 [propertyName=property;]*。注意 - 「使用者」和「密碼」屬性將明確傳遞,因此不需要包含在這裡。預設值為 |
poolPreparedStatements |
(boolean) 未使用的屬性。 |
maxOpenPreparedStatements |
(int) 未使用的屬性。 |
Tomcat JDBC 增強屬性
屬性 | 說明 |
---|---|
initSQL |
(String) 在首次建立連線時要執行的自訂查詢。預設值為 |
jdbcInterceptors |
(String) 擴充
這些攔截器將插入為
預定義的攔截器 更多預先定義的攔截器會在 JDBC 攔截器區段 中詳細說明。 |
validationInterval |
(long) 避免過度驗證,僅以這個頻率執行驗證 - 時間以毫秒為單位。如果連線需要驗證,但在此區間內已驗證過,則不會再次驗證。預設值為 |
jmxEnabled |
(boolean) 是否使用 JMX 註冊池。預設值為 |
fairQueue |
(boolean) 如果您希望以真正的 FIFO 方式公平處理對 getConnection 的呼叫,請設定為 true。這會使用 |
abandonWhenPercentageFull |
(int) 已放棄(逾時)的連線不會關閉,也不會回報,除非正在使用的連線數目高於 |
maxAge |
(長整數)以毫秒為單位,重新建立連線前保持連線的時間。當從池中借用連線時,池會檢查是否已達到 |
useEquals |
(布林值)如果您希望 |
suspectTimeout |
(整數)超時值(以秒為單位)。預設值為 |
rollbackOnReturn |
(布林值)如果 |
commitOnReturn |
(布林值)如果 |
alternateUsernameAllowed |
(布林值)預設情況下,jdbc-pool 會忽略
然而,可以設定池,以允許每次要求連線時使用不同的憑證。若要啟用 |
dataSource |
(javax.sql.DataSource) 將資料來源注入連線池,而池會使用資料來源來擷取連線,而不是使用 |
dataSourceJNDI |
(字串) 要在 JNDI 中查詢的資料來源的 JNDI 名稱,然後用來建立與資料庫的連線。請參閱 |
useDisposableConnectionFacade |
(布林值) 如果您希望在連線中放置一個外觀,以便在關閉後無法重複使用,請將此設定為 true。這可防止執行緒保留已呼叫關閉的連線參考,以便對其執行查詢。預設值為 |
logValidationErrors |
(布林值) 將此設定為 true 以將驗證階段中的錯誤記錄到日誌檔案。如果設定為 true,錯誤會記錄為 SEVERE。為了向後相容性,預設值為 |
propagateInterruptState |
(布林值) 將此設定為 true 以傳播已中斷執行緒的中斷狀態(不清除中斷狀態)。為了向後相容性,預設值為 |
ignoreExceptionOnPreLoad |
(布林值) 旗標,表示在初始化池時是否忽略連線建立錯誤。如果您要在初始化池時忽略連線建立錯誤,請設定為 true。如果您要透過擲出例外狀況讓池的初始化失敗,請設定為 false。預設值為 |
useStatementFacade |
(布林值) 如果您希望封裝陳述式以啟用已關閉陳述式上呼叫的 |
進階用法
JDBC 攔截器
若要查看如何使用攔截器的範例,請查看 org.apache.tomcat.jdbc.pool.interceptor.ConnectionState
。此簡單攔截器是三個屬性的快取,交易隔離層級、自動提交和唯讀狀態,以便系統避免不必要的資料庫往返。
隨著需求增加,將會新增更多攔截器到池的核心。隨時歡迎貢獻!
當然,攔截器不限於 java.sql.Connection
,也可以用來封裝方法呼叫的任何結果。您可以建立查詢效能分析器,在查詢執行時間超過預期時間時提供 JMX 通知。
組態 JDBC 攔截器
使用 jdbcInterceptors 屬性設定 JDBC 攔截器。此屬性包含分號分隔的類別名稱清單。如果類別名稱不是完全限定,它將加上 org.apache.tomcat.jdbc.pool.interceptor.
前綴。
範例
jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState; org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
等於
jdbcInterceptors="ConnectionState;StatementFinalizer"
攔截器也可以有屬性。攔截器的屬性在類別名稱後以括號指定。多個屬性以逗號分隔。
範例
jdbcInterceptors="ConnectionState;StatementFinalizer(useEquals=true)"
類別名稱、屬性名稱和值周圍的額外空白字元會被忽略。
org.apache.tomcat.jdbc.pool.JdbcInterceptor
所有攔截器的抽象基底類別,無法實例化。
屬性 | 說明 |
---|---|
useEquals |
(布林值) 如果您希望 |
org.apache.tomcat.jdbc.pool.interceptor.ConnectionState
快取下列屬性的連線:autoCommit
、readOnly
、transactionIsolation
和 catalog
。這是效能提升,可避免在呼叫 getter 或以已設定值呼叫 setter 時往返資料庫。
屬性 | 說明 |
---|
org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer
追蹤使用 createStatement
、prepareStatement
或 prepareCall
建立的所有陳述式,並在連線傳回池時關閉這些陳述式。
屬性 | 說明 |
---|---|
trace |
(布林值為字串) 啟用未關閉陳述式的追蹤。啟用後,如果連線已關閉,但陳述式未關閉,攔截器將記錄所有堆疊追蹤。預設值為 |
org.apache.tomcat.jdbc.pool.interceptor.StatementCache
在連線上快取 PreparedStatement
和/或 CallableStatement
實例。
這些陳述會依連線快取。計數限制會針對屬於同一個池的所有連線進行全球計算。一旦計數達到max
,後續陳述會不會傳回快取,並會立即關閉。
屬性 | 說明 |
---|---|
prepared |
(布林值為字串) 啟用快取使用 |
callable |
(布林值為字串) 啟用快取使用 |
max |
(int 為字串) 連線池中快取陳述會計數的限制。預設值為 |
org.apache.tomcat.jdbc.pool.interceptor.StatementDecoratorInterceptor
請參閱 48392。攔截器會包裝陳述會和結果集,以防止使用 ResultSet.getStatement().getConnection()
和 Statement.getConnection()
方法存取實際連線。
屬性 | 說明 |
---|
org.apache.tomcat.jdbc.pool.interceptor.QueryTimeoutInterceptor
在建立新陳述會時自動呼叫 java.sql.Statement.setQueryTimeout(seconds)
。池本身不會對查詢進行逾時處理,仍由 JDBC 驅動程式強制執行查詢逾時。
屬性 | 說明 |
---|---|
queryTimeout |
(int 為字串) 設定查詢逾時秒數。小於或等於零的值會停用此功能。預設值為 |
org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport
追蹤查詢效能,並在查詢超過失敗時間門檻時發出記錄條目。使用的記錄層級為 WARN
。
屬性 | 說明 |
---|---|
threshold |
(int 為字串) 查詢必須超過的毫秒數,才會發出記錄警示。預設值為 |
maxQueries |
(int 為字串) 為了保留記憶體空間,追蹤的最大查詢數。小於或等於 0 的值會停用此功能。預設值為 |
logSlow |
(布林值為字串) 如果您想要記錄緩慢查詢,請設定為 |
logFailed |
(布林值為字串) 如果您想要記錄失敗查詢,請設定為 |
org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx
延伸 SlowQueryReport
,除了記錄條目之外,還會發出 JMX 通知,供監控工具做出反應。繼承其父類別的所有屬性。此類別使用 Tomcat 的 JMX 引擎,因此在 Tomcat 容器外無法運作。預設情況下,如果啟用,JMX 通知會透過 ConnectionPool mbean 傳送。如果 notifyPool=false
,SlowQueryReportJmx
也可以註冊 MBean。
屬性 | 說明 |
---|---|
notifyPool |
(布林值為字串) 如果您想要 JMX 通知傳送至 |
objectName |
(字串) 定義一個有效的 |
org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer
放棄計時器會在從池中取出連線時啟動。這表示如果您有 30 秒的逾時時間,並使用連線執行 10 個 10 秒的查詢,則會將其標記為已放棄,並可能根據 abandonWhenPercentageFull
屬性進行回收。使用此攔截器,它會在您對連線執行作業或成功執行查詢時,重設結帳計時器。
屬性 | 說明 |
---|
程式碼範例
可以在 Tomcat 文件 中找到其他關於 JDBC 使用的 Tomcat 組態範例。
純 Java
以下是建立和使用資料來源的簡單範例。
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://127.0.0.1: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://127.0.0.1:3306/mysql"/>
非同步連線擷取
Tomcat JDBC 連線池支援非同步連線擷取,而不會將其他執行緒新增到池中程式庫。它會透過將方法新增到稱為 Future<Connection> getConnectionAsync()
的資料來源來執行此動作。為了使用非同步擷取,必須符合兩個條件
- 您必須將
fairQueue
屬性組態為true
。 - 您必須將資料來源轉型為
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");
攔截器
攔截器是一種強大的方式,可以用於啟用、停用或修改特定連線或其子元件的功能。攔截器有許多不同的使用案例。預設情況下,連線池是無狀態的,而且基於效能考量。池本身插入的唯一狀態是 defaultAutoCommit
、defaultReadOnly
、defaultTransactionIsolation
、defaultCatalog
(如果已設定)。這 4 個屬性只會在建立連線時設定。如果在使用連線期間修改這些屬性,池本身不會重設它們。
攔截器必須延伸 org.apache.tomcat.jdbc.pool.JdbcInterceptor
類別。此類別相當簡單,您需要有一個無引數建構函式
public JdbcInterceptor() {
}
當從池中借用連線時,攔截器可以透過實作
public abstract void reset(ConnectionPool parent, PooledConnection con);
方法來初始化或以其他方式對事件做出反應。此方法會使用兩個參數呼叫,一個是連線池本身的參考 ConnectionPool parent
,另一個是基礎連線的參考 PooledConnection con
。
當呼叫 java.sql.Connection
物件上的方法時,它會導致
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
要呼叫的方法。Method method
是實際呼叫的方法,而 Object[] args
是引數。我們示範如何讓呼叫 java.sql.Connection.close()
變成無效動作,如果連線已關閉,這是一個非常簡單的範例
if (CLOSE_VAL==method.getName()) {
if (isClosed()) return null; //noop for already closed.
}
return super.invoke(proxy,method,args);
有一個觀察。這是方法名稱的比較。執行此操作的方法之一是 "close".equals(method.getName())
。在上面,我們看到方法名稱和 static final String
參考之間的直接參考比較。根據 JVM 規格,方法名稱和 static final String
會出現在共享常數池中,因此參考比較應該會成功。當然也可以這樣做
if (compare(CLOSE_VAL,method)) {
if (isClosed()) return null; //noop for already closed.
}
return super.invoke(proxy,method,args);
compare(String,Method)
會在攔截器上使用 useEquals
旗標,並在設定 useEquals=true
旗標時執行參考比較或字串值比較。
池啟動/停止
當連線池啟動或關閉時,你可以收到通知。即使是執行個體方法,你也只會收到每個攔截器類別一次通知。而且你會收到通知,使用目前未附加到池的攔截器。
public void poolStarted(ConnectionPool pool) {
}
public void poolClosed(ConnectionPool pool) {
}
覆寫這些方法時,如果你要擴充 JdbcInterceptor
以外的類別,請別忘了呼叫 super
設定攔截器
攔截器使用 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 連線
連線池在實際連線周圍建立包裝器,以便適當地將它們集中起來。我們也會在這些包裝器中建立攔截器,以便能夠執行特定功能。如果需要擷取實際連線,可以使用 javax.sql.PooledConnection
介面。
Connection con = datasource.getConnection();
Connection actual = ((javax.sql.PooledConnection)con).getConnection();
建置
我們使用 1.6 建立 JDBC 池程式碼,但它向後相容到 1.5 的執行時間環境。對於單元測試,我們使用 1.6 和更高版本
可以在 Tomcat 文件 中找到其他關於 JDBC 使用的 Tomcat 組態範例。
從原始碼建置
建立非常簡單。池依賴於 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
可以在 Tomcat 原始碼存放庫中找到建置檔案。
為方便起見,也包含一個建置檔案,其中一個簡單的建置指令會產生所有需要的檔案。
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)
系統結構化為 Maven 建置,但會產生發行成品。只有函式庫本身。