2013-04-02 11 views
27

का उपयोग कर SQLException पर लेनदेन रोलबैक मुझे संसाधनों के साथ प्रयास करने में समस्या है और मैं बस सुनिश्चित होने के लिए कह रहा हूं। क्या मैं इसका उपयोग कर सकता हूं, अगर मुझे अपवाद पर प्रतिक्रिया करने की ज़रूरत है, और मुझे अभी भी पकड़ ब्लॉक में संसाधन की आवश्यकता है? एक कोशिश के साथ-संसाधनों में "पकड़ और अंत में ब्लॉक -नए प्रयास-संसाधन-संसाधन ब्लॉक

try (java.sql.Connection con = createConnection()) 
{ 
    con.setAutoCommit(false); 
    Statement stm = con.createStatement(); 
    stm.execute(someQuery); // causes SQLException 
} 
catch(SQLException ex) 
{ 
    con.rollback(); 
    // do other stuff 
} 

मुझे डर है कि मैं अभी भी इस मामले में-अंत में कोशिश पकड़ पुराने उपयोग करने के लिए बर्बाद कर रहा हूँ, यहाँ तक कि ओरेकल प्रलेखन के अनुसार: दिए गए उदाहरण यह है बयान, घोषित संसाधन बंद होने के बाद किसी भी पकड़ या आखिरकार ब्लॉक चलाया जाता है। "

+2

यदि इस मामले में कनेक्शन स्वयं विफल हो गया है, तो इसे वापस रोल करने का कोई मतलब नहीं है। 'Con' का दायरा केवल ब्लॉक को सीमित करने के लिए सीमित है। – learningloop

+0

यह प्रश्न भी मदद कर सकता है। http://stackoverflow.com/questions/9260159/java-7-automatic-resource-management-jdbc –

+0

दिए गए सभी रोचक विकल्पों में से, मैं अभी भी मूल 'कोशिश-पकड़-अंत में' – Adam

उत्तर

33

भाषा की कल्पना के अनुसार, कनेक्शन क्लॉज निष्पादित होने से पहले कनेक्शन बंद हो जाएगा (http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.3.2)।

सम्भावित समाधान घोंसला कोशिश के साथ-संसाधनों बयान करने के लिए है:

try (java.sql.Connection con = createConnection()) 
{ 
    con.setAutoCommit(false); 
    try (Statement stm = con.createStatement()) 
    { 
     stm.execute(someQuery); // causes SQLException 
    } 
    catch(SQLException ex) 
    { 
     con.rollback(); 
     con.setAutoCommit(true); 
     throw ex; 
    } 
    con.commit(); 
    con.setAutoCommit(true); 
} 

उम्मीद है कि उस बिंदु को दिखाता है। यदि आप इसे उत्पादन कोड में उपयोग करने की योजना बनाते हैं तो इसे थोड़ा सा सुधारना चाहिए।

उदाहरण के लिए, यदि आप कनेक्शन पूल का उपयोग कर रहे हैं, तो आपको कनेक्शन प्राप्त करना होगा जैसा कि आपको मिला, इसलिए con.setAutoCommit (true); अंत में खंड में किया जाना चाहिए। इसका मतलब यह होगा कि बाहरी प्रयास-संसाधनों को अंततः पारंपरिक प्रयास-पकड़ होना चाहिए।

+0

अच्छा समाधान पसंद करता हूं, thx –

+1

हो जानते हैं कि ['setAutoCommit'] को कॉल करना (http://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html#setAutoCommit-boolean-) किसी भी लंबित लेनदेन पर एक COMMIT कॉल करता है। तो अधिक जटिल कोड में सावधान रहें। –

+1

"इसका मतलब यह होगा कि बाहरी प्रयासों के साथ-साथ संसाधनों को पारंपरिक प्रयास-अंत होना चाहिए।" - वास्तव में "संसाधनों के साथ प्रयास करें" पैटर्न कनेक्शन पूल के साथ जेडीबीसी कनेक्शन के लिए पूरी तरह से बेकार है ...? – Itai

3

उपरोक्त उदाहरण में मुझे लगता है कि con.commit() को नेस्टेड try-catch के अंदर रखना बेहतर है क्योंकि यह SQLException फेंक सकता है।

try (java.sql.Connection con = createConnection()) 
    { 
     con.setAutoCommit(false); 
     try (Statement stm = con.createStatement()) 
     { 
      stm.execute(someQuery); // causes SQLException 
      con.commit();   // also causes SQLException! 
     } 
     catch(SQLException ex) 
     { 
      con.rollback(); 
      throw ex; 
     }finally{ 
      con.setAutoCommit(true); 
     } 
    } 

हमें हमारे उत्पादन वातावरण में अनजान सत्रों में ऐसी समस्या थी।

+2

आपको ऑटोकॉमिट सेट करने के लिए अंत में कथन का उपयोग करना चाहिए सच में वापस। यह आपको इस कथन को दो बार रोकने के लिए रोक देगा। – AxelH

6
//try with resources 
    try(Connection conn = this.connectionProvider.getConnection()){//auto close BEFORE reach this , catch block, so we need a inner try block for statement 
     boolean oldAutoCommit=conn.getAutoCommit(); 
     conn.setAutoCommit(false);//auto commit to false 
     try(
      Statement stm = con.createStatement() 
     ){ 
      stm.execute(someQuery); // causes SQLException 
      conn.commit();//commit 
     } 
     catch (SQLException ex){ 
      conn.rollback();//error, rollback 
      throw ex;//If you need to throw the exception to the caller 
     } 
     finally { 
      conn.setAutoCommit(oldAutoCommit);//reset auto commit 
     } 
    } 
10

अपने कोड में आप ऑटोकॉमेट रीसेट करने के लिए "SQLException" को पकड़ रहे हैं। किसी भी प्रकार का रनटाइम अपवाद (जैसे एक शून्य सूचक अपवाद) ऑटो-प्रतिबद्धता को रीसेट किए बिना आपके कोड से बबल होगा।

कोशिश-के-संसाधन वाक्यविन्यास संकलक को सभी निष्पादन पथों को कवर करने के लिए कुछ अद्भुत कोड उत्पन्न करने और बंद करने के माध्यम से सभी दबाए गए अपवादों को बनाए रखने का कारण बनता है। सहायक वर्गों के एक जोड़े के साथ आप के लिए प्रतिबद्ध/रोलबैक डालें और रीसेट-autocommit कोड निर्माण की प्रक्रिया में कर सकते हैं:

import java.sql.SQLException; 
import java.sql.Connection; 

public class AutoRollback implements AutoCloseable { 

    private Connection conn; 
    private boolean committed; 

    public AutoRollback(Connection conn) throws SQLException { 
     this.conn = conn;   
    } 

    public void commit() throws SQLException { 
     conn.commit(); 
     committed = true; 
    } 

    @Override 
    public void close() throws SQLException { 
     if(!committed) { 
      conn.rollback(); 
     } 
    } 

} 

public class AutoSetAutoCommit implements AutoCloseable { 

    private Connection conn; 
    private boolean originalAutoCommit; 

    public AutoSetAutoCommit(Connection conn, boolean autoCommit) throws SQLException { 
     this.conn = conn; 
     originalAutoCommit = conn.getAutoCommit(); 
     conn.setAutoCommit(autoCommit); 
    } 

    @Override 
    public void close() throws SQLException { 
     conn.setAutoCommit(originalAutoCommit); 
    } 

} 

अब आप रोलबैक को नियंत्रित करने और इस तरह वाक्य रचना "संसाधन के साथ प्रयास करें" के साथ autocommit कर सकते हैं:

try(Connection conn = getConnection(), 
     AutoSetAutoCommit a = new AutoSetAutoCommit(conn,false), 
     AutoRollback tm = new AutoRollback(conn)) 
    { 

     // Do stuff 

     tm.commit(); 
    } 
+1

दिलचस्प कोड, मैंने ऑटोक्लोसेबल इंटरफ़ेस को आजमाने के लिए समय नहीं लिया। यह आखिरकार खंड को प्रतिस्थापित करने के लिए एक अच्छा अनुमोदन प्रतीत होता है। उत्पादन में कोड बहुत अधिक होगा केवल एक कोशिश के साथ-साथ संसाधन के साथ आसान। अच्छा! – AxelH

+0

1. आप क्यों घोषणा नहीं करते "एसक्यू फेंकता है LException "करीबी() तरीकों में? आप सिर्फ एक अपवाद के रूप में क्यों लपेटते हैं? 2. आप एक ही प्रयास ब्लॉक के भीतर सभी संसाधन क्यों नहीं बनाते? AFAIK वे एक विपरीत क्रम में बंद हो जाएगा, आप उन्हें घोषित कर दिया है। – Sabine

+0

बहुत अच्छे अंक! मैं एसक्यूएल अपवादों को फेंकने के लिए बंद कर दूंगा और उन्हें एक कोशिश में जोड़ दूंगा जैसा कि आप और एक्सेलएच ने सुझाव दिया था। प्रतिक्रिया के लिए धन्यवाद। – ChrisCantrell