這是最近讀到的講關(guān)于 JDBC 的超時(shí)問(wèn)題最透徹的文章,原文是http://www.cubrid.org/blog/understanding-jdbc-internals-and-timeout-configuration 懦鼠,網(wǎng)上現(xiàn)有的翻譯感覺(jué)磕磕絆絆的粱快,很多上下文信息丟失了,這里用我的理解重新翻譯一下沿腰。
應(yīng)用程序中配置恰當(dāng)?shù)?JDBC 超時(shí)時(shí)間能減少服務(wù)失敗的時(shí)間览徒,這篇文章我們將討論不同種類(lèi)的超時(shí)和推薦的配置。
Web 應(yīng)用服務(wù)器在 DDoS 攻擊后變得無(wú)響應(yīng)
(這是一個(gè)真實(shí)案例的發(fā)生過(guò)程復(fù)述)
在 DDoS 攻擊之后颂龙,整個(gè)服務(wù)都不能正常工作了习蓬,因?yàn)榈谒膶咏粨Q機(jī)不能工作,網(wǎng)絡(luò)連接斷開(kāi)了措嵌,這也導(dǎo)致 WAS (可以將 WAS 理解為作者公司的應(yīng)用程序)不能正常工作躲叼。攻擊發(fā)生后不久,安全團(tuán)隊(duì)攔截了所有 DDoS 攻擊企巢,然后網(wǎng)絡(luò)恢復(fù)正常枫慷,但 WAS 還是不能工作。
通過(guò)分析系統(tǒng)的 dump 日志發(fā)現(xiàn)浪规,業(yè)務(wù)系統(tǒng)停在了 JDBC API 的調(diào)用上或听。20分鐘后系統(tǒng)仍處于等待狀態(tài)無(wú)法響應(yīng),大概過(guò)了30分鐘笋婿,系統(tǒng)突然發(fā)生異常誉裆,然后服務(wù)恢復(fù)正常。
為什么已經(jīng)將查詢(xún)超時(shí)時(shí)間設(shè)置成3秒缸濒, WAS 卻等待了30分鐘足丢?為什么30分鐘后 WAS 又開(kāi)始工作了?
如果理解了 JDBC 的超時(shí)機(jī)制就能找到答案庇配。
為什么我們需要知道 JDBC 驅(qū)動(dòng)
當(dāng)有性能問(wèn)題或系統(tǒng)級(jí)錯(cuò)誤時(shí)斩跌,WAS 和數(shù)據(jù)庫(kù)是我們關(guān)注的兩個(gè)重要層面。在我公司 WAS 和數(shù)據(jù)庫(kù)通常由不同的部門(mén)負(fù)責(zé)讨永,因此每個(gè)部門(mén)聚焦在各自負(fù)責(zé)的領(lǐng)域來(lái)設(shè)法弄清楚狀況滔驶。此時(shí) WAS 和數(shù)據(jù)庫(kù)之間的部分會(huì)因?yàn)榈貌坏阶銐虻年P(guān)注而產(chǎn)生盲區(qū)。對(duì)于 Java 應(yīng)用卿闹,這個(gè)盲區(qū)在數(shù)據(jù)庫(kù)連接池和 JDBC 之間揭糕,本文我們將重點(diǎn)討論 JDBC萝快。
什么是 JDBC 驅(qū)動(dòng)
JDBC 是 Java 應(yīng)用程序中用于訪問(wèn)數(shù)據(jù)庫(kù)的一套標(biāo)準(zhǔn) API,Sun 公司定義了4種類(lèi)型的 JDBC 驅(qū)動(dòng)著角。我公司主要用的是第4種揪漩,該類(lèi)型驅(qū)動(dòng)由純 Java 語(yǔ)言編寫(xiě),在 Java 應(yīng)用中通過(guò) socket 與數(shù)據(jù)庫(kù)通信吏口。
類(lèi)型4驅(qū)動(dòng)是通過(guò) socket 來(lái)處理字節(jié)流的奄容,它的基本操作和 HttpClient 這種網(wǎng)絡(luò)操作類(lèi)庫(kù)相同。同其他網(wǎng)絡(luò)類(lèi)庫(kù)一樣产徊,也會(huì)在發(fā)生超時(shí)的時(shí)候占用大量的 CPU 資源從而失去響應(yīng)昂勒。如果你之前用過(guò) HttpClient ,肯定遇到過(guò)因?yàn)闆](méi)有設(shè)置超時(shí)導(dǎo)致的錯(cuò)誤舟铜。如果 socket 超時(shí)設(shè)置不合適戈盈,類(lèi)型4驅(qū)動(dòng)也可能有同樣的錯(cuò)誤(連接被阻塞)。
下面讓我們了解如何配置 JDBC 驅(qū)動(dòng)的 socket 超時(shí)谆刨,以及設(shè)置時(shí)需考慮哪些問(wèn)題塘娶。
WAS 與數(shù)據(jù)庫(kù)間的設(shè)置超時(shí)的層次
圖2展示了簡(jiǎn)化的 WAS 和數(shù)據(jù)庫(kù)通信時(shí)的超時(shí)層次。
更上層的超時(shí)依賴(lài)于下層的超時(shí)痊夭,只有當(dāng)較低層的超時(shí)機(jī)制正常工作刁岸,上層的超時(shí)才會(huì)正常。如果 JDBC 驅(qū)動(dòng)程序的 socket 超時(shí)工作不正常她我,那么更上層的超時(shí)比如 Statement 超時(shí)和事務(wù)超時(shí)都不會(huì)正常工作虹曙。
我們收到很多評(píng)論說(shuō):
即使配置了 Statement 超時(shí),應(yīng)用程序還是不能從故障中恢復(fù)鸦难,因?yàn)?Statement 超時(shí)在網(wǎng)絡(luò)故障時(shí)不起作用根吁。
Statement 超時(shí)在網(wǎng)絡(luò)故障時(shí)不起作用。它只能做到:限制一次Statement 執(zhí)行的時(shí)間合蔽,處理超時(shí)以防網(wǎng)絡(luò)故障必須由 JDBC 驅(qū)動(dòng)來(lái)做。
JDBC 驅(qū)動(dòng)的 socket 超時(shí)還會(huì)受操作系統(tǒng)的 socket 超時(shí)配置的影響介返。這解釋了為什么案例中的 JDBC 連接在網(wǎng)絡(luò)故障后阻塞了30分鐘才恢復(fù)拴事,即使沒(méi)配置 JDBC 驅(qū)動(dòng)的 socket 超時(shí)。
DBCP 連接池位于圖2的左邊圣蝎。你會(huì)發(fā)現(xiàn)各種層面的超時(shí)與 DBCP 是分開(kāi)的刃宵。DBCP 負(fù)責(zé)數(shù)據(jù)庫(kù)連接(即本文中說(shuō)到的Connection)的創(chuàng)建和管理,并不涉及超時(shí)的處理徘公。當(dāng)在 DBCP 中創(chuàng)建了一個(gè)數(shù)據(jù)庫(kù)連接或發(fā)送了一條查詢(xún)校驗(yàn)的 sql 語(yǔ)句用于檢查連接有效性時(shí)牲证,socket 超時(shí)會(huì)影響這些過(guò)程的處理,但并不直接影響應(yīng)用程序关面。
然而在應(yīng)用程序中調(diào)用 DBCP 的 getConnection() 方法時(shí)坦袍,你能指定應(yīng)用程序獲取數(shù)據(jù)庫(kù)連接的超時(shí)時(shí)間十厢,但這和 JDBC 的連接超時(shí)無(wú)關(guān)。
什么是事務(wù)超時(shí)
事務(wù)超時(shí)是在框架(Spring捂齐、EJB容器)或應(yīng)用程序?qū)用嫔喜庞行У某瑫r(shí)蛮放。
事務(wù)超時(shí)可能是個(gè)不常見(jiàn)的概念。簡(jiǎn)單講奠宜,事務(wù)超時(shí)等于** Statement 超時(shí) * N(需要執(zhí)行的 Statement 的數(shù)量) + 其它(垃圾回收等其他時(shí)間)**包颁。事務(wù)超時(shí)被用來(lái)限制執(zhí)行一個(gè)事務(wù)之內(nèi)所有 Statement 執(zhí)行的總時(shí)長(zhǎng)。
比如压真,假設(shè)執(zhí)行一次 Statement 執(zhí)行需0.1秒娩嚼,那執(zhí)行幾次 Statement
并不是什么問(wèn)題,但如果是執(zhí)行十萬(wàn)次則需要一萬(wàn)秒(大約7個(gè)小時(shí))滴肿,這就可以用上事務(wù)超時(shí)了待锈。
EJB 的聲明式事務(wù)管理 (容器管理事務(wù)) 就是一種典型的使用場(chǎng)景,但聲明式事務(wù)管理只是定義了相應(yīng)的規(guī)范嘴高,容器內(nèi)事務(wù)的處理過(guò)程和具體實(shí)現(xiàn)由容器的開(kāi)發(fā)者負(fù)責(zé)竿音。我們公司并沒(méi)有用 EJB,用的是最常見(jiàn)的 Spring 框架拴驮,所以事務(wù)超時(shí)的配置也由 Spring 來(lái)管理春瞬。在 Spring 中,事務(wù)超時(shí)可以在 XML 文件顯式配置或在 Java 代碼中用 Transactional 注解來(lái)配置套啤。
<tx:attributes>
<tx:method name="…" timeout="3"/>
</tx:attributes>
Spring 提供的事務(wù)超時(shí)的配置非常簡(jiǎn)單宽气,它會(huì)記錄每個(gè)事務(wù)的開(kāi)始時(shí)間和消耗時(shí)間,當(dāng)特定的事件發(fā)生時(shí)會(huì)對(duì)已消耗掉的時(shí)間做校驗(yàn)潜沦,如果超出了配置將拋出異常萄涯。
Spring 中數(shù)據(jù)庫(kù)連接被保存在線程本地變量(ThreadLocal)中,這被稱(chēng)作事務(wù)同步(Transaction Synchronization)唆鸡。當(dāng)數(shù)據(jù)庫(kù)連接被保存到 ThreadLocal 時(shí)涝影,同時(shí)會(huì)記錄事務(wù)的開(kāi)始時(shí)間和超時(shí)時(shí)間。所以通過(guò)數(shù)據(jù)庫(kù)連接的代理創(chuàng)建的 Statement 在執(zhí)行時(shí)就會(huì)校驗(yàn)這個(gè)時(shí)間争占。
EJB 的聲明式事務(wù)管理的實(shí)現(xiàn)也是類(lèi)似燃逻,實(shí)現(xiàn)的思路非常簡(jiǎn)單。如果事務(wù)超時(shí)非常重要臂痕,但你所使用的容器或框架不提供此功能伯襟,你也可以選擇自己實(shí)現(xiàn),關(guān)于事務(wù)超時(shí)并沒(méi)有制定標(biāo)準(zhǔn)的 API握童。
Lucy 框架的1.5和1.6版不支持事務(wù)超時(shí)姆怪,但你可以通過(guò) Spring 的事務(wù)管理達(dá)到相同的效果。
假設(shè)一個(gè)事務(wù)里有5條 Statement ,每條 Statement 執(zhí)行時(shí)間是200毫秒稽揭,其它業(yè)務(wù)邏輯或框架操作的執(zhí)行時(shí)間是100毫秒俺附,那事務(wù)允許的超時(shí)時(shí)間至少應(yīng)該1100毫秒(200 * 5 + 100)。
什么是 Statement 超時(shí)
Statement 超時(shí)是用來(lái)限制 Statement 的執(zhí)行時(shí)間的淀衣,它的具體值是通過(guò) JDBC API 來(lái)設(shè)置的昙读。JDBC 驅(qū)動(dòng)程序基于這個(gè)值進(jìn)行 Statement 執(zhí)行時(shí)的超時(shí)處理。Statement 超時(shí)是通過(guò) JDBC API 中java.sql.Statement 類(lèi)的 setQueryTimeout(int timeout) 方法配置的膨桥。不過(guò)現(xiàn)在的開(kāi)發(fā)者已經(jīng)很少直接在代碼中配置它了蛮浑,更多是通過(guò)框架來(lái)進(jìn)行設(shè)置。
以 iBatis 為例只嚣,可以通過(guò) SqlMapConfig.xml 中的 setting 屬性defaultStatementTimeout 來(lái)設(shè)置全局的 statement 超時(shí)缺省值沮稚。你也可以通過(guò)在具體的 sql 映射文件中的 select insert update 標(biāo)簽的 statement 屬性來(lái)覆蓋。
當(dāng)你用 Lucy 1.5或1.6版時(shí)册舞,可以通過(guò)設(shè)置 queryTimeout 屬性在數(shù)據(jù)源層面設(shè)置 Statement 超時(shí)蕴掏。
Statement 超時(shí)的具體數(shù)值需要根據(jù)每個(gè)應(yīng)用自身的情況而定,并沒(méi)有推薦的配置调鲸。
JDBC 驅(qū)動(dòng)中的 Statement 超時(shí)處理過(guò)程
每個(gè)數(shù)據(jù)庫(kù)和驅(qū)動(dòng)程序的 Statement 超時(shí)的處理也是不同的盛杰。Oracle 和 SQLServer 的工作方式比較像,MySQL 和 CUBRID 比較像藐石。
Oracle 中的 Statement 超時(shí)處理
- 調(diào)用 Connection 的 createStatement() 方法創(chuàng)建一個(gè) Statement 對(duì)象
- 調(diào)用 Statement 的 executeQuery() 方法
- Statement 通過(guò)內(nèi)部綁定的 Connection 對(duì)象將查詢(xún)命令發(fā)送到 Oracle 數(shù)據(jù)庫(kù)
- Statement 向 Oracle 的超時(shí)處理線程 OracleTimeoutPollingThread(每個(gè)類(lèi)加載器一個(gè)該線程)注冊(cè)一個(gè) Statement 用于處理超時(shí)
- 發(fā)生超時(shí)
- Oracle 的 OracleTimeoutPollingThread 調(diào)用 OracleStatement 的 cancel() 方法
- 通過(guò) Statement 的 Connection 發(fā)送一條消息取消還在執(zhí)行的查詢(xún)
JTDS (MS SQLServer) 中的 Statement 超時(shí)處理
1.調(diào)用 Connection 的 createStatement() 方法創(chuàng)建一個(gè) Statement 對(duì)象
- 調(diào)用 Statement 的 executeQuery() 方法
- Statement 通過(guò)內(nèi)部的 Connection 將查詢(xún)命令發(fā)送到 MS SqlServer 數(shù)據(jù)庫(kù)
- Statement 向 MS SQLServer 的 TimerThread 線程注冊(cè)一個(gè) Statement 用于處理超時(shí)
- 發(fā)生超時(shí)
- TimerThread 調(diào)用 JtdsStatement 內(nèi)部的 TsdCore.cancel()方法
- 通過(guò) ConnectionJDBC 發(fā)送一條消息取消還在執(zhí)行的查詢(xún)
MySQL (5.0.8) 中的 Statement 超時(shí)處理
- 調(diào)用 Connection 的 createStatement() 方法創(chuàng)建一個(gè) Statement 對(duì)象
- 調(diào)用 Statement 的 executeQuery() 方法
- Statement 通過(guò)內(nèi)部的 Connection 將查詢(xún)命令傳輸?shù)? MySqlServer 數(shù)據(jù)庫(kù)
- Statement 創(chuàng)建一個(gè)新的超時(shí)執(zhí)行線程(timeout-execution)來(lái)處理超時(shí)
- 5.1以上版本改為每個(gè)連接分配一個(gè)線程
- 向 timeout-execution 線程注冊(cè)當(dāng)前的 Statement
- 發(fā)生超時(shí)
- timeout-execution 線程創(chuàng)建一個(gè)相同配置的 Connection
- 用新創(chuàng)建的 Connection 發(fā)送取消查詢(xún)的命令
CUBRID中的 Statement 超時(shí)處理
- 調(diào)用 Connection 的 createStatement() 方法創(chuàng)建一個(gè) Statement 對(duì)象
- 調(diào)用 Statement 的 executeQuery() 方法
- Statement 通過(guò)內(nèi)部的 Connection 將查詢(xún)命令發(fā)送到 CUBRID 數(shù)據(jù)庫(kù)
- Statement 創(chuàng)建一個(gè)新的超時(shí)執(zhí)行線程(timeout-execution)來(lái)處理超時(shí)
- 向 timeout-execution 線程注冊(cè)當(dāng)前的 Statement
- 發(fā)生超時(shí)
- timeout-execution 線程創(chuàng)建一個(gè)相同配置的Connection
- 用新創(chuàng)建的 Connection 發(fā)送取消查詢(xún)的命令
什么是 Socket 超時(shí)
類(lèi)型4的 JDBC 驅(qū)動(dòng)是用 Socket 方式與數(shù)據(jù)庫(kù)連接的即供,應(yīng)用程序和數(shù)據(jù)庫(kù)之間的連接超時(shí)并不是由數(shù)據(jù)庫(kù)處理的。
當(dāng)數(shù)據(jù)庫(kù)突然宕掉或發(fā)生網(wǎng)絡(luò)錯(cuò)誤(設(shè)備故障等)時(shí)于微,JDBC 驅(qū)動(dòng)的 Socket 超時(shí)的值是必須的昼榛。由于 TCP/IP 的結(jié)構(gòu)脆烟,Socket 沒(méi)有辦法檢測(cè)到網(wǎng)絡(luò)錯(cuò)誤,因此應(yīng)用不能檢測(cè)到與數(shù)據(jù)庫(kù)到連接斷開(kāi)了啊掏。如果沒(méi)有設(shè)置 Socket 超時(shí)冷离,應(yīng)用程序會(huì)一直等待數(shù)據(jù)庫(kù)返回結(jié)果度帮。(這個(gè)連接也被叫做“死連接”) 為了避免死連接唧领,Socket 必須要設(shè)置超時(shí)時(shí)間焰手。Socket 超時(shí)可以通過(guò) JDBC 驅(qū)動(dòng)程序配置。通過(guò)設(shè)置 Socket 超時(shí)吗坚,可以防止出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤時(shí)一直等待的情況并縮短故障時(shí)間祈远。
不推薦使用 Socket 超時(shí)來(lái)限制一個(gè) Statement 的執(zhí)行時(shí)間,因此Socket 超時(shí)的值必須要高于 Statement 的超時(shí)時(shí)間商源,否則 Socket 超時(shí)將會(huì)先生效,這樣 Statement 超時(shí)就沒(méi)有意義谋减,也無(wú)法生效牡彻。
下面展示了 Socket 超時(shí)設(shè)置的連個(gè)選項(xiàng),其配置因不同的驅(qū)動(dòng)而異。
- Socket 連接時(shí)的超時(shí):通過(guò) Socket 對(duì)象的 connect(SocketAddress endpoint, int timeout) 方法來(lái)配置
- Socket 讀寫(xiě)時(shí)的超時(shí):通過(guò) Socket 對(duì)象的 setSoTimeout(int timeout) 方法來(lái)配置
通過(guò)查看CUBRID庄吼,MySQL缎除,MS SQL Server (JTDS) 和 Oracle 的JDBC 驅(qū)動(dòng)源碼,我們確認(rèn)以上所有驅(qū)動(dòng)都是使用上面的2個(gè) API 來(lái)設(shè)置socket 超時(shí)的总寻。
下面列出了如何配置 Socket 超時(shí)
JDBC 驅(qū)動(dòng) | 連接超時(shí)配置 | Socket 超時(shí)配置 | JDBC Url 格式 | 示例 |
---|---|---|---|---|
MySQL | connectTimeout(默認(rèn)值:0器罐,單位:毫秒) | socketTimeout(默認(rèn)值:0,單位:ms) | jdbc:mysql://[host:port],[host:port].../[database] [?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]... |
jdbc:mysql://xxx.xx.xxx.xxx:3306/database?connectTimeout=60000&socketTimeout=60000 |
MS-SQL , jTDS | loginTimeout(默認(rèn)值:0渐行,單位:秒) | socketTimeout(默認(rèn)值:0轰坊,單位:s) | jdbc:jtds:<server_type>://<server>[:<port>][/<database>][;<property>=<value>[;...]] | jdbc:jtds:sqlserver://server:port/database;loginTimeout=60;socketTimeout=60 |
Oracle | oracle.net.CONNECT_TIMEOUT (默認(rèn)值:0,單位:毫秒) | oracle.jdbc.ReadTimeout(默認(rèn)值:0祟印,單位:毫秒) | 不支持通過(guò)url配置肴沫,只能通過(guò)OracleDatasource.setConnectionProperties() API設(shè)置,使用DBCP時(shí)可以調(diào)用BasicDatasource.setConnectionProperties()或BasicDatasource.addConnectionProperties()進(jìn)行設(shè)置 | - |
CUBRID | 無(wú)單獨(dú)配置項(xiàng)(默認(rèn)值:5,000蕴忆,單位:毫秒) | 無(wú)單獨(dú)配置項(xiàng)(默認(rèn)值:5,000颤芬,單位:毫秒) | - | - |
- connectTimeout 和 socketTimeout 的默認(rèn)值是 0 ,這意味著不會(huì)發(fā)生超時(shí)套鹅。
- 你也可以通過(guò)屬性進(jìn)行配置站蝠,而無(wú)需直接使用 DBCP 的 API 。
通過(guò)屬性進(jìn)行配置時(shí)卓鹿,需要傳入的 key 為 "connectionProperties"菱魔,其 value 的格式為" [propertyName=property;]*"。下面是 iBatis 中通過(guò) xml 文件配置屬性的例子减牺。
<transactionManager type="JDBC">
<dataSource type="com.nhncorp.lucy.db.DbcpDSFactory">
....
<property name="connectionProperties" value="oracle.net.CONNECT_TIMEOUT=6000;oracle.jdbc.ReadTimeout=6000"/>
</dataSource>
</transactionManager>
操作系統(tǒng)層面的 Socket 超時(shí)配置
如果沒(méi)設(shè)置 Socket 超時(shí)或連接超時(shí)豌习,應(yīng)用程序多數(shù)情況下無(wú)法檢測(cè)到網(wǎng)絡(luò)錯(cuò)誤。此時(shí)拔疚,應(yīng)用程序?qū)⒁恢钡却氯シ事。钡竭B接上數(shù)據(jù)庫(kù)或能讀取到數(shù)據(jù)。然而稚失,如果查看實(shí)際服務(wù)遇到的實(shí)際情況會(huì)發(fā)現(xiàn)問(wèn)題常常在在應(yīng)用程序(WAS)在30分鐘后嘗試重新連接到網(wǎng)絡(luò)后被解決了栋艳。這是因?yàn)椴僮飨到y(tǒng)也配置了 Socket 超時(shí)時(shí)間。我公司使用的 Linux 服務(wù)器將 Socket 超時(shí)時(shí)間設(shè)置為30分鐘句各。它將在操作系統(tǒng)層面對(duì)網(wǎng)絡(luò)連接做校驗(yàn)吸占。因?yàn)楣镜?Linux 服務(wù)器的 KeepAlive 檢查周期為30分鐘,因此即使應(yīng)用程序里將 Socket 超時(shí)設(shè)置為0凿宾,由網(wǎng)絡(luò)原因引起的數(shù)據(jù)庫(kù)網(wǎng)絡(luò)連接問(wèn)題也不會(huì)超過(guò)30分鐘矾屯。
通常,應(yīng)用程序會(huì)在調(diào)用 Socket 的 read() 方法時(shí)由于網(wǎng)絡(luò)問(wèn)題而阻塞住初厚。然而很少在調(diào)用 Socket 的 write() 方法時(shí)處于等待狀態(tài)件蚕,這取決于網(wǎng)絡(luò)構(gòu)成和錯(cuò)誤類(lèi)型。當(dāng)應(yīng)用程序調(diào)用 Socket 的 write() 方法時(shí),數(shù)據(jù)被記錄到操作系統(tǒng)的內(nèi)核緩沖區(qū)排作,然后將控制權(quán)立即交還給應(yīng)用程序牵啦。因此,一旦數(shù)據(jù)已經(jīng)寫(xiě)入內(nèi)核緩沖區(qū)妄痪,write() 的調(diào)用始終是成功哈雏。但是,如果操作系統(tǒng)內(nèi)核緩沖區(qū)由于特殊的網(wǎng)絡(luò)錯(cuò)誤而滿了的話衫生,write() 方法也會(huì)進(jìn)入等待狀態(tài)裳瘪。這種情況下,操作系統(tǒng)會(huì)嘗試重新發(fā)送數(shù)據(jù)包一段時(shí)間障簿,并在達(dá)到超時(shí)限制時(shí)產(chǎn)生錯(cuò)誤盹愚。 在公司的 Linux服務(wù)器上這種情況的超時(shí)時(shí)間設(shè)置為15分鐘。
至此站故,我已經(jīng)解釋了 JDBC 的內(nèi)部操作皆怕,希望這將幫助你正確的超時(shí)配置超時(shí)時(shí)間從而減少錯(cuò)誤。
至此西篓,我已經(jīng)對(duì)JDBC的內(nèi)部操作做了講解愈腾,希望能夠讓大家學(xué)會(huì)如何正確的配置超時(shí)時(shí)間,從而減少錯(cuò)誤的發(fā)生岂津。