未正確關閉數據庫連接
![]() |
自增長整數型字段賦值表 |
申請了數據庫連接,卻沒有及時關閉,這是最常見的數據庫連接使用方面的錯誤。犯這種錯誤的原因很多,以下是常見的一種比較低級的錯誤:
public void foo() {Connection conn=getConnection();Statement stmt = null;try {conn=getConnection();stmt=conn.createStatement(); } catch(Exception e) { } finally {close(stmt, conn); }} |
在上述案例中的第2行代碼中,作者申請了一個Connection,但在第6行代碼中,又申請了一個新的,并且丟失了第一次申請的 Connection的引用。至此,當程序每調一次Foo方法,將導致申請一個新的Connection而沒有釋放它。因此,當數據庫達到最大連接數時,將導致整個應用的運行失敗。
避免這種錯誤的方法有很多,譬如,可采用類似于FindBugs的代碼分析工具對應用的源碼進行分析,找出可能產生錯誤的代碼。
此外,在應用中,我們要頻繁地對申請的數據庫連接進行關閉與釋放。此時,建議封裝成某些工具類使用,并且要盡可能安全地關閉數據庫連接。
任意申請數據庫連接
不考慮事務上下文,任意申請數據庫連接資源也是常見的不當用法。但這種問題往往是難以克服的,根源在于Java是一種面向對象的語言,而數據庫的事務卻是一種批量化的操作過程。我們以常見的序列號的實現方案為例:在某些應用場景中,我們需要一種自增長的整數型字段。但由于不同的數據庫有不同的實現,所以,為達到各個數據庫兼容的目的,我們常用的解決方案是,新建一張T_SEQUENCE表,它可能包含的字段有:NAME varchar(100), CURRENT_VAL number(10);其中,NAME存放序列的名稱,而CURRENT_VAL存放序列的當前值。假設某一業務對象Customer需要新增一筆記錄時,為獲得不重復且自增長的Customer ID,需要將T_SEQUENCE表中與該業務表對應的序列號加1并更新,然后將更新后的值作為Customer的ID。我們以面向對象的3種方法來實現:
public class Customer {/更新序列號使其加1/public void sequencePlus(){Connection conn=null; Statement stmt =null; ……//將T_SEQUENCE的序列號當前值加1;}/獲取當前序列號/public int getSequenceCurrentVal(){Connection conn=null;Statement stmt=null;ResultSet rset =null;……// 獲取當前的序列號值;} /新增一條Customer記錄,自動根據序列號生成主鍵/public void addCustomer(String name) {Connection conn=null;PreparedStatement stmt = null;ResultSet rset=null;sequencePlus();// 序列號加1;int id = getSequenceCurrentVal(); // 得到當前序列號;…….// 將最新序列號作為新的T_Customer記錄的主鍵插入;} } |
針對這種應用場景,我們首先需要認識到:上述3個方法應該屬于同一個數據庫事務。否則,在并發情況下,將出現由于主鍵重復而導致數據插入失敗的情況。但同時,我們也需要看到:即便上述3個方法的執行位于同一個事務中,但3個方法使用的是不同的數據庫連接,雖然在sequencePlus方法中將 T_SEQUENCE表中的數據加1 ,但在事務并未提交的情況下,由于Connection隔離級別的原因,在getSequenceCurrentVal方法中,是看不到 sequencePlus方法中更新以后的數據的。這樣,也將導致數據插入失敗,因為主鍵勢必跟舊有ID值重復。
因此,傳統編程方法為克服上述問題,只有在上述的方法中使用同一個Connection,才能夠保證業務數據的正確
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com