การปิดการเชื่อมต่อ JDBC ในพูล


109

ส่วนรหัสมาตรฐานของเราสำหรับการใช้ JDBC คือ ...

Connection conn = getConnection(...);
Statement  stmt = conn.conn.createStatement (ResultSet.TYPE_SCROLL_INSENSITIVE,
                                                ResultSet.CONCUR_READ_ONLY);
ResultSet  rset = stmt.executeQuery (sqlQuery);

// do stuff with rset

rset.close(); stmt.close(); conn.close();

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

คำถามที่ 2: วิธีการต่อไปนี้ใกล้เคียงกับมาตรฐานหรือไม่? ดูเหมือนจะพยายามรับการเชื่อมต่อจากพูลและหากไม่สามารถสร้างแหล่งข้อมูลได้ให้ใช้ DriverManager แบบเก่า เราไม่แน่ใจด้วยซ้ำว่าส่วนใดถูกเรียกใช้ในรันไทม์ ถามคำถามข้างบนซ้ำเราควรปิด Connection ที่มาจากวิธีดังกล่าวหรือไม่?

ขอบคุณ - มส.

synchronized public Connection getConnection (boolean pooledConnection)
                                                        throws SQLException {
        if (pooledConnection) {
                if (ds == null) {
                        try {
                                Context envCtx = (Context)
                                        new InitialContext().lookup("java:comp/env");
                                ds = (DataSource) envCtx.lookup("jdbc/NamedInTomcat");
                                return ds.getConnection();
                        } catch (NamingException e) {
                                e.printStackTrace();
                }}
                return (ds == null) ? getConnection (false) : ds.getConnection();
        }
        return DriverManager.getConnection(
                "jdbc:mysql://"+ipaddy+":"+dbPort +"/" + dbName, uName, pWord);
}

แก้ไข: ฉันคิดว่าเราได้รับการเชื่อมต่อแบบรวมเนื่องจากเราไม่เห็นการติดตามสแต็ก

คำตอบ:


121

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

ใช่แน่นอนคุณต้องปิดการเชื่อมต่อแบบรวมด้วย มันเป็นกระดาษห่อหุ้มรอบการเชื่อมต่อจริง ภายใต้ฝาปิดจะปล่อยการเชื่อมต่อจริงกลับไปที่สระว่ายน้ำ นอกจากนี้ยังขึ้นอยู่กับพูลในการตัดสินใจว่าจะปิดการเชื่อมต่อจริงหรือจะใช้ซ้ำสำหรับการgetConnection()โทรใหม่ ดังนั้นไม่ว่าคุณกำลังใช้สระว่ายน้ำการเชื่อมต่อหรือไม่คุณควรเสมอใกล้ทรัพยากรทั้งหมด JDBC ในการสั่งซื้อที่ตรงกันข้ามในfinallyบล็อกของtryบล็อกที่คุณได้มาพวกเขา ใน Java 7 สามารถทำให้ง่ายขึ้นโดยใช้try-with-resourcesคำสั่ง


วิธีต่อไปนี้ใกล้เคียงกับมาตรฐานหรือไม่? ดูเหมือนจะพยายามรับการเชื่อมต่อจากพูลและหากไม่สามารถสร้างแหล่งข้อมูลได้ให้ใช้ DriverManager แบบเก่า เราไม่แน่ใจด้วยซ้ำว่าส่วนใดถูกเรียกใช้ในรันไทม์ ถามคำถามข้างบนซ้ำเราควรปิด Connection ที่มาจากวิธีดังกล่าวหรือไม่?

ตัวอย่างน่ากลัวทีเดียว คุณเพียงแค่ต้องค้นหา / เริ่มต้นDataSourceเพียงครั้งเดียวในระหว่างการเริ่มต้นของแอปพลิเคชันในตัวสร้าง / การเริ่มต้นของคลาสการกำหนดค่าฐานข้อมูลทั่วแอปพลิเคชัน จากนั้นเพียงโทรหาgetConnection()แหล่งข้อมูลเดียวตลอดอายุการใช้งานที่เหลือของแอปพลิเคชัน ไม่จำเป็นต้องซิงโครไนซ์หรือ nullchecks

ดูสิ่งนี้ด้วย:


นั่นคือสิ่งที่กำลังทำอยู่ (เริ่มต้นครั้งเดียว) ไม่ใช่หรือ ds เป็นตัวแปรอินสแตนซ์และถ้า (ds == null) ... เป็นส่วนเริ่มต้น
Manidip Sengupta

การตรวจสอบทุกครั้งด้วยวิธีการรับgetConnection()เป็นเรื่องแปลก เพียงแค่ทำใน c'tor หรือ initialization block ของคลาสเดียวกันโดยไม่ต้องซิงโครไนซ์ / nullchecks จะเรียกเพียงครั้งเดียว สำหรับคำแนะนำเพิ่มเติมและตัวอย่างการเริ่มต้นคุณอาจพบว่าบทความนี้มีประโยชน์
BalusC

บทความยอดเยี่ยม BalusC ชั้นเรียนที่ฉันกำลังจัดการกับชั้นข้อมูลค่อนข้างมากโดยใช้ DTO ฉันเห็นด้วยกับคุณการเริ่มต้นควรอยู่ในตัวสร้าง ตอนนี้คลาสนี้มีเมธอดมากมายโดยแต่ละตัวมี conn, stmt และ rset เป็นตัวแปรโลคัลการเชื่อมต่ออยู่ในบล็อกการลองและในที่สุดก็มีการโทร 1 บรรทัด csrClose (conn, stmt, rset) โดยที่ทั้ง 3 ถูกปิด (ในลำดับย้อนกลับ) ตอนนี้ DTO ที่คุณพัฒนาในตัวอย่างคือภาพสะท้อนของแถวตาราง DB เรามีแบบสอบถาม SQL ที่ซับซ้อนพร้อมการรวม (และส่วนคำสั่งอื่น ๆ ) คุณมีบทความเกี่ยวกับวิธีการพัฒนา DAO สำหรับผลลัพธ์ดังกล่าวหรือไม่?
Manidip Sengupta

2
@yat: คุณต้องเรียกclose()พวกเขาทั้งหมดในfinallyบล็อกของtryบล็อกเดียวกับที่คุณได้มา / สร้างขึ้น ทั้งหมดนี้ไม่ว่าจะเป็นการเชื่อมต่อแบบรวมหรือไม่ก็ตาม
BalusC

1
@iJava: สระว่ายน้ำนี้เขียนโดยมือสมัครเล่นที่ไม่รู้ว่าเขากำลังทำอะไรอยู่ ละเว้นและไปที่ห้องสมุดจริง เช่น HikariCP.
BalusC

22

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

วิธีการปิด () อาจมีลักษณะดังนี้:

public void close() throws SQLException {
  pool.returnConnection(this);
}

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


ฉันเห็นด้วยเรามีคนตัดไม้และสามารถใช้ที่นี่ได้เช่นกัน ฉันต้องการศึกษาเล็กน้อยเกี่ยวกับวิธีที่คุณสามารถตัด Object แทนที่วิธีการ close () แต่ยังคงรักษาชื่อคลาสเดิมไว้ (Connection)
Manidip Sengupta

1
Calling close() is OK and probably still required.การไม่โทรปิดจะทำให้การเชื่อมต่อรั่วไหลเว้นแต่ว่าพูลจะใช้กลยุทธ์การกู้คืน
svarog

0

ที่จริงแล้วแนวทางที่ดีที่สุดในการจัดการการเชื่อมต่อคือการไม่ใช้รหัสใด ๆ

สร้างคลาส SQLExecutor ที่เป็นตำแหน่งเดียวที่เปิดและปิดการเชื่อมต่อ

จากนั้นแอปพลิเคชันที่เหลือทั้งหมดจะปั๊มคำสั่งไปยังตัวดำเนินการแทนที่จะได้รับการเชื่อมต่อจากพูลและจัดการ (หรือจัดการไม่ถูกต้อง) ทั่วทุกที่

คุณสามารถมีอินสแตนซ์ของตัวดำเนินการได้มากเท่าที่คุณต้องการ แต่ไม่ควรมีใครเขียนโค้ดที่เปิดและปิดการเชื่อมต่อในนามของตัวเอง

นอกจากนี้ยังช่วยให้คุณสามารถบันทึก SQL ทั้งหมดของคุณจากรหัสชุดเดียว

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