การปิดการเชื่อมต่อฐานข้อมูลใน Java


121

ฉันเริ่มสับสนเล็กน้อยฉันอ่านด้านล่างจากhttp://en.wikipedia.org/wiki/Java_Database_Connectivity

Connection conn = DriverManager.getConnection(
     "jdbc:somejdbcvendor:other data needed by some jdbc vendor",
     "myLogin",
     "myPassword" );

Statement stmt = conn.createStatement();
try {
    stmt.executeUpdate( "INSERT INTO MyTable( name ) VALUES ( 'my name' ) " );
} finally {
    //It's important to close the statement when you are done with it
    stmt.close();
}

คุณไม่จำเป็นต้องปิด conn Connection หรือไม่? จะเกิดอะไรขึ้นถ้า conn.close () ไม่เกิดขึ้น?

ฉันมีเว็บแอปส่วนตัวที่ฉันดูแลอยู่ซึ่งตอนนี้ยังไม่ได้ปิดแบบฟอร์มทั้งสองแบบ แต่สิ่งที่สำคัญจริงๆคือ stmt one, conn one หรือทั้งสองอย่าง?

ไซต์หยุดทำงานเป็นระยะ ๆ แต่เซิร์ฟเวอร์ยังคงบอกว่ามันเป็นปัญหาการเชื่อมต่อฐานข้อมูลความสงสัยของฉันคือมันไม่ได้ถูกปิด แต่ฉันไม่รู้ว่าจะปิดตัวไหนดี


เป็นแนวทางปฏิบัติที่ดีที่สุดเสมอในการปิดการเชื่อมต่อด้วยตัวคุณเองโดยไม่ต้องขึ้นอยู่กับไดรเวอร์และเทมเพลตอื่น ๆ ในการจัดการการปิด ความล้มเหลวในการปิดการเชื่อมต่อจะส่งผลให้ซ็อกเก็ตและทรัพยากรเปิดตลอดไปจนกว่าจะหยุดทำงาน (ไม่มีสถานการณ์ของทรัพยากรอีกต่อไป) หรือรีสตาร์ท
Arun Joshla

คำตอบ:


196

เมื่อคุณใช้งานของคุณเสร็จConnectionแล้วคุณจำเป็นต้องปิดมันอย่างชัดเจนโดยเรียกclose()ใช้เมธอดเพื่อปล่อยทรัพยากรฐานข้อมูลอื่น ๆ (เคอร์เซอร์ที่จับ ฯลฯ ) ที่การเชื่อมต่ออาจยึดอยู่

ที่จริงรูปแบบปลอดภัยใน Java คือการปิดของคุณResultSet, StatementและConnection(ตามลำดับ) ในfinallyบล็อกเมื่อคุณทำกับพวกเขาสิ่งที่ต้องการ:

Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;

try {
    // Do stuff
    ...

} catch (SQLException ex) {
    // Exception handling stuff
    ...
} finally {
    if (rs != null) {
        try {
            rs.close();
        } catch (SQLException e) { /* ignored */}
    }
    if (ps != null) {
        try {
            ps.close();
        } catch (SQLException e) { /* ignored */}
    }
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) { /* ignored */}
    }
}

finallyบล็อกได้ดีขึ้นเล็กน้อยลงไป (เพื่อหลีกเลี่ยงการตรวจสอบเป็นโมฆะ):

} finally {
    try { rs.close(); } catch (Exception e) { /* ignored */ }
    try { ps.close(); } catch (Exception e) { /* ignored */ }
    try { conn.close(); } catch (Exception e) { /* ignored */ }
}

แต่ถึงกระนั้นนี่ก็เป็นสิ่งที่ละเอียดมากดังนั้นโดยทั่วไปคุณจะใช้คลาสตัวช่วยเพื่อปิดอ็อบเจ็กต์ด้วยวิธีตัวช่วยที่ไม่ปลอดภัยและfinallyบล็อกจะกลายเป็นแบบนั้น:

} finally {
    DbUtils.closeQuietly(rs);
    DbUtils.closeQuietly(ps);
    DbUtils.closeQuietly(conn);
}

และที่จริงแล้วApache Commons DbUtilsมีDbUtilsคลาสที่ทำอย่างนั้นได้อย่างแม่นยำจึงไม่จำเป็นต้องเขียนของคุณเอง


3
ความช่วยเหลือที่ยอดเยี่ยมขอบคุณ! ฉันไม่เข้าใจหรือคิดถึงคำสั่ง conn! = null
onaclov2000

1
@ onaclov2000 ใช่rs, ps, connอาจจะnullขึ้นอยู่กับที่แบ่งรหัส นั่นเป็นสาเหตุที่เรียกว่ารูปแบบ "ปลอดภัย"
Pascal Thivent

12
@Pascal Thivent: จริงๆแล้วเราไม่จำเป็นต้องปิดทั้งหมด หนังสือ "Core Java Volume 2 - Advanced Features" เขียนว่า: closeวิธีการของStatementวัตถุจะปิดการเชื่อมโยงโดยอัตโนมัติResultSetหากคำสั่งมีชุดผลลัพธ์ที่เปิดอยู่ ในทำนองเดียวกันcloseเมธอดของConnectionคลาสจะปิดStatementsไฟล์Connection.
Majid Azimi

12
@Majid: เว้นแต่จะเป็นการเชื่อมต่อแบบรวม งบก็จะรั่วไหลไป
BalusC

1
@BalusC: คุณช่วยอธิบายได้ไหมว่าจะเกิดอะไรขึ้นเมื่อการเชื่อมต่อแบบรวมถูกปิดโดยใช้วิธี connection.close ()
Krsna Chaitanya

61

ควรปิดฐานข้อมูล / วัตถุทรัพยากรหลังการใช้งานเสมอ ดีกว่าที่จะปิดการเชื่อมต่อชุดผลลัพธ์และวัตถุคำสั่งในfinallyบล็อก

จนกว่าจะถึง Java7 ทรัพยากรทั้งหมดเหล่านี้จำเป็นต้องปิดโดยใช้finallyบล็อก หากคุณใช้ Java 7 ดังนั้นสำหรับการปิดรีซอร์สคุณสามารถทำได้ดังนี้

try(Connection con = getConnection(url, username, password, "org.postgresql.Driver");
    Statement stmt = con.createStatement();
    ResultSet rs = stmt.executeQuery(sql);
) {

//statements
}catch(....){}

ตอนนี้วัตถุ con, stmt และ rs กลายเป็นส่วนหนึ่งของ try block และ java จะปิดทรัพยากรเหล่านี้โดยอัตโนมัติหลังการใช้งาน

หวังว่าฉันจะเป็นประโยชน์


จะเกิดอะไรขึ้นถ้าคำสั่งของฉันเป็นนัยเช่นResultSet rs = conn.createStatement().executeQuery(sql);อยู่ในtryบล็อก?
Antares42

1
คุณจะไม่สามารถอ้างอิงได้ใน {} บล็อกสุดท้ายสำหรับการปิด หากมีข้อยกเว้นเกิดขึ้นเมธอด close () ของ ResultSet จะไม่ถูกเรียก
แดน

จะเกิดอะไรขึ้นถ้าฉันไม่ปิดมัน?
Alex78191

หากคุณไม่ปิดมันหน่วยความจำรั่วอาจเกิดขึ้นได้
Yadu Krishnan

14

ก็พอที่จะปิดเพียงและStatement Connectionไม่จำเป็นต้องปิดResultSetวัตถุอย่างชัดเจน

เอกสาร Java กล่าวเกี่ยวกับjava.sql.ResultSet:

อ็อบเจ็กต์ ResultSet ถูกปิดโดยอัตโนมัติโดยอ็อบเจ็กต์ Statement ที่สร้างขึ้นเมื่ออ็อบเจ็กต์ Statement ถูกปิดดำเนินการใหม่หรือใช้เพื่อดึงผลลัพธ์ถัดไปจากลำดับของผลลัพธ์หลาย ๆ


ขอบคุณ BalusC สำหรับความคิดเห็น: "ฉันจะไม่พึ่งพาสิ่งนั้นไดรเวอร์ JDBC บางตัวล้มเหลวในเรื่องนั้น"


25
ฉันจะไม่พึ่งพาสิ่งนั้น ไดรเวอร์ JDBC บางตัวล้มเหลวในเรื่องนั้น เช่น Oracle ที่มี "เคอร์เซอร์เปิดเกินสูงสุด" เป็นต้นเพียงแค่ปิดทรัพยากรที่เปิดทั้งหมดอย่างชัดเจนไม่มีข้อแก้ตัว
BalusC

1
ฉันไม่อยากใช้ไดรเวอร์ที่ไม่เป็นไปตามข้อกำหนด
Enerccio

2
ดังที่ BalusC ชี้ให้เห็นการตั้งโปรแกรมป้องกันที่ดีในการปิดการเชื่อมต่ออย่างชัดเจนแทนที่จะต้องเดินสายเชื่อมต่อกับผู้ให้บริการรายใดรายหนึ่ง
michaelok

11

ใช่. คุณต้องปิดชุดผลลัพธ์คำสั่งและการเชื่อมต่อ หากการเชื่อมต่อมาจากพูลการปิดจริงจะส่งกลับไปที่พูลเพื่อใช้ซ้ำ

โดยทั่วไปคุณต้องทำสิ่งนี้ในfinally{}บล็อกเช่นหากมีข้อยกเว้นเกิดขึ้นคุณก็ยังมีโอกาสปิดสิ่งนี้ได้

กรอบงานจำนวนมากจะดูแลปัญหาการจัดสรร / การจัดสรรทรัพยากรนี้ให้คุณ เช่นฤดูใบไม้ผลิJdbcTemplate Apache DbUtilsมีวิธีการในการดูแลการปิดชุดผลลัพธ์ / คำสั่ง / การเชื่อมต่อว่าเป็นโมฆะหรือไม่ (และการจับข้อยกเว้นเมื่อปิด) ซึ่งอาจช่วยได้เช่นกัน


1
เมื่อฉันแทรกคราส "ในที่สุด" ชอบที่จะเน้นบอกฉันว่ามันผิด สิ่งนี้ควรดำเนินต่อไปหลังจากบล็อกจับหรือไม่
onaclov2000

ใช่. try {} จับ {} {} ที่สุด การจับ {} เป็นทางเลือก btw เช่นเดียวกับในที่สุด {}
Brian Agnew

ฉันย้ายคำสั่ง "ปิด" ไปในที่สุด แต่มีเพียงแค่พูดว่า "sqlexception" มีข้อเสนอแนะไหม
onaclov2000

1
ปิด () พ่น SQLException คุณต้องจัดการกับสิ่งนั้น ดู DbUtils.closeQuietly () เพื่อจัดการสิ่งนี้อย่างเงียบ ๆ
Brian Agnew

> จะเกิดอะไรขึ้นถ้า conn.close () ไม่เกิดขึ้น?
Alex78191

8

ที่จริงแล้วจะเป็นการดีที่สุดหากคุณใช้บล็อก try-with-resources และ Java จะปิดการเชื่อมต่อทั้งหมดให้คุณเมื่อคุณออกจากบล็อกลอง

คุณควรทำเช่นนี้กับวัตถุใด ๆ ที่ใช้ AutoClosable

try (Connection connection = getDatabaseConnection(); Statement statement = connection.createStatement()) {
    String sqlToExecute = "SELECT * FROM persons";
    try (ResultSet resultSet = statement.execute(sqlToExecute)) {
        if (resultSet.next()) {
            System.out.println(resultSet.getString("name");
        }
    }
} catch (SQLException e) {
    System.out.println("Failed to select persons.");
}

การเรียกใช้ getDatabaseConnection เพิ่งสร้างขึ้น แทนที่ด้วยการโทรที่ทำให้คุณได้รับการเชื่อมต่อ JDBC SQL หรือการเชื่อมต่อจากพูล


ดังนั้นคุณไม่จำเป็นต้องปิดการเชื่อมต่อด้วยตนเองในกรณีนี้?
Colin D

1
แก้ไข. คุณไม่จำเป็นต้องปิดการเชื่อมต่ออย่างชัดเจน จะปิดเมื่อถึงจุดสิ้นสุดของบล็อกรหัสการลอง
โจ

7

ใช่คุณต้องปิดการเชื่อมต่อ มิฉะนั้นไคลเอนต์ฐานข้อมูลมักจะเปิดการเชื่อมต่อซ็อกเก็ตและทรัพยากรอื่น ๆ ไว้


... จนกว่าจะออก สิ่งนี้จะเชื่อมโยงทรัพยากร จำกัด ต่างๆทั้งฝั่งไคลเอ็นต์และเซิร์ฟเวอร์ หากไคลเอนต์ทำสิ่งนี้มากเกินไปอาจทำให้เกิดปัญหากับตัวไคลเอนต์บริการฐานข้อมูลและอาจเกิดขึ้นกับแอปพลิเคชันอื่น ๆ ที่ทำงานบนเครื่องไคลเอนต์หรือเซิร์ฟเวอร์
Stephen C
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.