java中sql去除游标_java.sql.SQLException:-ORA-01000:已超过最大打开游标
小編典典
ORA-01000(最大打開游標錯誤)是Oracle數(shù)據(jù)庫開發(fā)中極為常見的錯誤。在Java上下文中,當應(yīng)用程序嘗試打開比數(shù)據(jù)庫實例上配置的游標更多的ResultSet時,就會發(fā)生這種情況。
常見原因有:
配置錯誤
在應(yīng)用程序中,查詢數(shù)據(jù)庫的線程比數(shù)據(jù)庫中的游標的線程更多。一種情況是你的連接和線程池大于數(shù)據(jù)庫上的游標數(shù)。
你有許多開發(fā)人員或應(yīng)用程序連接到同一個數(shù)據(jù)庫實例(可能包含許多架構(gòu)),并且一起使用的連接過多。
解:
增加數(shù)據(jù)庫上的游標數(shù)量(如果資源允許)或
減少應(yīng)用程序中的線程數(shù)。
游標泄漏
應(yīng)用程序未關(guān)閉ResultSet(在JDBC中)或游標(在數(shù)據(jù)庫上的存儲過程中)
解決方案:游標泄漏是錯誤;增加數(shù)據(jù)庫上的游標數(shù)量只會延遲不可避免的故障。可以使用靜態(tài)代碼分析,JDBC或應(yīng)用程序級日志記錄以及數(shù)據(jù)庫監(jiān)視來發(fā)現(xiàn)泄漏。
背景
本節(jié)描述了游標背后的一些理論以及應(yīng)如何使用JDBC。如果你不需要了解背景,可以跳過此步驟,直接進入“消除泄漏”。
什么是游標?
游標是數(shù)據(jù)庫上的資源,用于保存查詢的狀態(tài),特別是讀取器在ResultSet中的位置。每個SELECT語句都有一個游標,并且PL / SQL存儲過程可以打開并根據(jù)需要使用任意數(shù)量的游標。你可以在Orafaq上找到有關(guān)游標的更多信息。
數(shù)據(jù)庫實例通常服務(wù)于幾種不同的模式,許多不同的用戶各自具有多個會話。為此,它具有可用于所有模式,用戶和會話的固定數(shù)量的游標。當所有游標都處于打開狀態(tài)(使用中)并且要求新游標的請求進入時,請求失敗,并出現(xiàn)ORA-010000錯誤。
查找和設(shè)置光標數(shù)量
該號碼通常由DBA在安裝時配置。可以在Oracle SQL Developer的管理員功能中訪問當前使用的游標數(shù)量,最大數(shù)量和配置。在SQL中,可以使用以下命令進行設(shè)置:
ALTER SYSTEM SET OPEN_CURSORS=1337 SID='*' SCOPE=BOTH;
將JVM中的JDBC與數(shù)據(jù)庫上的游標相關(guān)聯(lián)
下面的JDBC對象與以下數(shù)據(jù)庫概念緊密相關(guān):
JDBC 連接是數(shù)據(jù)庫會話的客戶端表示形式,并提供數(shù)據(jù)庫事務(wù)。一個連接一次只能打開一個事務(wù)(但是事務(wù)可以嵌套)
數(shù)據(jù)庫上的單個游標支持JDBC ResultSet。在ResultSet上調(diào)用close()時,將釋放光標。
JDBC CallableStatement調(diào)用數(shù)據(jù)庫上的存儲過程,通常以PL / SQL編寫。該存儲過程可以創(chuàng)建零個或多個游標,并且可以將游標作為JDBC ResultSet返回。
JDBC是線程安全的:在線程之間傳遞各種JDBC對象是完全可以的。
例如,你可以在一個線程中創(chuàng)建連接。另一個線程可以使用此連接來創(chuàng)建PreparedStatement,第三個線程可以處理結(jié)果集。唯一的主要限制是,你隨時都不能在一個PreparedStatement上打開多個ResultSet。請參見Oracle DB每個連接是否支持多個(并行)操作?
請注意,數(shù)據(jù)庫提交發(fā)生在連接上,因此該連接上的所有DML(INSERT,UPDATE和DELETE)都將一起提交。因此,如果要同時支持多個事務(wù),則每個并發(fā)事務(wù)必須至少具有一個Connection。
關(guān)閉JDBC對象
執(zhí)行ResultSet的典型示例是:
Statement stmt = conn.createStatement();
try {
ResultSet rs = stmt.executeQuery( "SELECT FULL_NAME FROM EMP" );
try {
while ( rs.next() ) {
System.out.println( "Name: " + rs.getString("FULL_NAME") );
}
} finally {
try { rs.close(); } catch (Exception ignore) { }
}
} finally {
try { stmt.close(); } catch (Exception ignore) { }
}
請注意,finally子句如何忽略close()引發(fā)的任何異常:
如果你只關(guān)閉ResultSet而沒有使用try {} catch {},則它可能會失敗并阻止Statement被關(guān)閉
我們希望允許嘗試主體中引發(fā)的任何異常傳播到調(diào)用方。如果有一個循環(huán),例如創(chuàng)建和執(zhí)行語句,請記住關(guān)閉循環(huán)中的每個語句。
在Java 7中,Oracle引入了AutoCloseable接口,該接口用一些漂亮的語法糖代替了大多數(shù)Java 6樣板。
持有JDBC對象
JDBC對象可以安全地保存在局部變量,對象實例和類成員中。通常更好的做法是:
使用對象實例或類成員來保存可以在更長的時間內(nèi)多次重用的JDBC對象,例如Connections和PreparedStatements
將局部變量用于ResultSet,因為通常會在單個函數(shù)范圍內(nèi)獲取,循環(huán)并關(guān)閉它們。
但是,有一個例外:如果你正在使用EJB或Servlet / JSP容器,則必須遵循嚴格的線程模型:
只有Application Server創(chuàng)建線程(用于處理傳入請求)
只有Application Server創(chuàng)建連接(你可以從連接池中獲得)
在兩次調(diào)用之間保存值(狀態(tài))時,必須非常小心。永遠不要將值存儲在你自己的緩存或靜態(tài)成員中-這在群集和其他怪異條件下并不安全,并且Application Server可能會對你的數(shù)據(jù)造成可怕的后果。而是使用有狀態(tài)Bean或數(shù)據(jù)庫。
特別是,永遠不要通過不同的遠程調(diào)用來保存JDBC對象(連接,結(jié)果集,PreparedStatements等)-讓Application Server對此進行管理。Application Server不僅提供連接池,而且還緩存你的PreparedStatements。
消除泄漏
有許多可用于幫助檢測和消除JDBC泄漏的過程和工具:
在開發(fā)過程中-盡早發(fā)現(xiàn)錯誤是迄今為止的最佳方法:
開發(fā)實踐:良好的開發(fā)實踐應(yīng)在軟件離開開發(fā)人員之前減少軟件中的錯誤數(shù)量。具體做法包括:
1. 配對編程,以教育沒有足夠經(jīng)驗的人
2. 代碼審查,因為許多眼睛勝過一只眼睛
3. 單元測試,這意味著你可以使用測試工具來練習所有代碼庫,從而使重現(xiàn)泄漏變得微不足道
4. 使用現(xiàn)有的庫進行連接池,而不是構(gòu)建自己的庫
靜態(tài)代碼分析:使用出色的Findbugs之類的工具來執(zhí)行靜態(tài)代碼分析。這會拾取許多未正確處理close()的地方。Findbugs有一個用于Eclipse的插件,但也可以一次性運行,并已集成到Jenkins CI和其他構(gòu)建工具中
在運行時:
可保持性和提交
如果ResultSet的可保存性為ResultSet.CLOSE_CURSORS_OVER_COMMIT,則在調(diào)用Connection.commit()方法時關(guān)閉ResultSet。可以使用Connection.setHoldability()或使用重載的Connection.createStatement()方法進行設(shè)置。
2. 在運行時記錄。
在你的代碼中放置良好的日志語句。這些內(nèi)容應(yīng)該清晰易懂,以便客戶,支持人員和隊友無需培訓即可理解。它們應(yīng)簡潔,并包括打印關(guān)鍵變量和屬性的狀態(tài)/內(nèi)部值,以便你可以跟蹤處理邏輯。良好的日志記錄是調(diào)試應(yīng)用程序(尤其是已部署的應(yīng)用程序)的基礎(chǔ)。
你可以在項目中添加調(diào)試JDBC驅(qū)動程序(用于調(diào)試-請勿實際部署)。一個示例(我還沒有使用過)是log4jdbc。然后,你需要對此文件進行一些簡單的分析,以查看哪些執(zhí)行沒有相應(yīng)的關(guān)閉。計算打開和關(guān)閉應(yīng)該突出顯示是否存在潛在問題
監(jiān)視數(shù)據(jù)庫。使用諸如SQL Developer的“ Monitor SQL”功能或Quest的TOAD之類的工具監(jiān)視正在運行的應(yīng)用程序。本文介紹了監(jiān)視。在監(jiān)視期間,你查詢打開的游標(例如,從表v $ sesstat中)并查看其SQL。如果游標的數(shù)量在增加,并且(最重要的是)由一個相同的SQL語句控制,則你知道該SQL泄漏。搜索你的代碼并查看。
其他想法
你可以使用WeakReferences處理關(guān)閉的連接嗎?
弱引用和軟引用是允許你以允許JVM在其認為合適的任何時間對對象進行垃圾收集的方式來引用對象的方法(假定該對象沒有強大的引用鏈)。
如果將構(gòu)造函數(shù)中的ReferenceQueue傳遞給軟引用或弱引用,則當對象發(fā)生GC對象時(如果根本發(fā)生),該對象將被放置在ReferenceQueue中。使用這種方法,你可以與對象的終結(jié)處理進行交互,并且可以在此時關(guān)閉或終結(jié)該對象。
幻像引用有些古怪;它們的目的僅是控制最終確定,但是你永遠無法獲得對原始對象的引用,因此很難在其上調(diào)用close()方法。
但是,嘗試控制何時運行GC并不是一個好主意(Weak,Soft和PhantomReferences 在對象已排隊進入GC 之后讓你知道)。實際上,如果JVM中的內(nèi)存量很大(例如-Xmx2000m),則可能永遠不會對對象進行GC,并且仍然會遇到ORA-01000。如果JVM內(nèi)存相對于程序要求而言較小,則可能會發(fā)現(xiàn)ResultSet和PreparedStatement對象在創(chuàng)建后立即被GC(在你可以從它們讀取之前),這很可能會使程序失敗。
TL; DR:弱引用機制不是管理和關(guān)閉Statement和ResultSet對象的好方法。
2020-03-01
總結(jié)
以上是生活随笔為你收集整理的java中sql去除游标_java.sql.SQLException:-ORA-01000:已超过最大打开游标的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 28岁戴牙套有用吗
- 下一篇: 隐形牙套每天要戴多少小时