7.1 พูลการเชื่อมต่อ

วันนี้เราจะเรียนรู้วิธีการทำงานกับฐานข้อมูลอย่างมืออาชีพมากยิ่งขึ้น และตอนนี้เราจะพูดถึงกลุ่มเธรดหรือกลุ่มการเชื่อมต่อในภาษาอังกฤษ

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

เพื่อแก้ปัญหาดังกล่าว จึงเสนอให้จัดเก็บการเชื่อมต่อกับฐานในบางชุด ซึ่งเรียกว่าเธรดพูล

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

อันที่จริงแล้ว แอปพลิเคชันทำงานผ่านไดรเวอร์พิเศษ ซึ่งเป็นตัวตัดสำหรับไดรเวอร์ JDBC ปกติ และมีฟังก์ชันเพิ่มเติมสำหรับการทำงานกับพูล สถานการณ์นี้สามารถแสดงได้ดังนี้:

พูลการเชื่อมต่อ

โปรแกรมเมอร์สามารถตั้งค่าสำหรับพูลการเชื่อมต่อได้ด้วยตนเอง: จำนวนการเชื่อมต่อที่ใช้งานอยู่ การหมดเวลา ฯลฯ

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

7.2* อินเทอร์เฟซ DataSource และ ConnectionPoolDataSource

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

วัตถุ DataSource ใช้เพื่อรับการเชื่อมต่อทางกายภาพกับฐานข้อมูลและเป็นทางเลือกแทน DriverManager ไม่จำเป็นต้องลงทะเบียนไดรเวอร์ JDBC คุณจะต้องตั้งค่าพารามิเตอร์ที่เหมาะสมเพื่อสร้างการเชื่อมต่อและดำเนินการเมธอด getConnection().

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

ดังนั้น เมื่อทำงานกับ Oracle DBMS เพื่อใช้ set และ get method ที่สอดคล้องกัน จึงจำเป็นต้องได้รับอ็อบเจกต์ประเภท OracleDataSource ซึ่งเป็นอินสแตนซ์ของคลาสชื่อเดียวกันที่ใช้อินเทอร์เฟซ DataSource ดังนั้น วิธีการสร้างออบเจกต์ประเภท DataSource นี้ทำให้โค้ดของคุณพกพาได้น้อยลงและขึ้นอยู่กับการใช้งานไดรเวอร์ที่เฉพาะเจาะจงน้อยลง

ต่อไปนี้คือตัวอย่างโค้ดที่แสดงการใช้วัตถุประเภท DataSource ในเครื่อง

import java.sql.*;
import javax.sql.*;
import oracle.jdbc.driver.*;
import oracle.jdbc.pool.*;

public class DataSource {
    public static void main(String[] args) {
    	try {
        	OracleDataSource ods = new OracleDataSource();

        	ods.setUser("root");
        	ods.setPassword("secret");
        	ods.setDriverType("thin");
        	ods.setDatabaseName("test");
        	ods.setServerName("localhost");
        	ods.setPortNumber(1521);

        	Connection connection = ods.getConnection();
        	System.out.println("Connection successful!!!");

    	} catch (SQLException se) {
        	se.printStackTrace();
    	}
    	System.out.println("Goodbye!");
	}
}

อินเทอร์เฟซ 7.3 * JNDI

ความสามารถทั้งหมดของออบเจกต์ประเภท DataSource นั้นแสดงร่วมกับการใช้อินเทอร์เฟซ JNDI JNDI เป็นบริการพิเศษ (เช่น ไดเร็กทอรี) สำหรับแอปพลิเคชันเซิร์ฟเวอร์ขนาดใหญ่ที่อนุญาตให้บริการหนึ่งสร้างการเชื่อมต่อกับอีกบริการหนึ่ง

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

ชื่อทรัพย์สิน ชนิดข้อมูลจาวา วัตถุประสงค์
ชื่อฐานข้อมูล สตริง ชื่อฐานข้อมูล
ชื่อเซิร์ฟเวอร์ สตริง ชื่อเซิร์ฟเวอร์
ผู้ใช้ สตริง ชื่อผู้ใช้ (ID)
รหัสผ่าน สตริง รหัสผ่านผู้ใช้
หมายเลขพอร์ต นานาชาติ หมายเลขพอร์ตเซิร์ฟเวอร์ DBMS

การรวมกันของอินเทอร์เฟซ DataSource และ JNDI มีบทบาทสำคัญในการพัฒนาแอปพลิเคชันระดับองค์กรแบบหลายชั้นโดยใช้เทคโนโลยีส่วนประกอบ J2EE

หากคุณใช้อินเทอร์เฟซ DataSource และ JNDI ร่วมกันในโค้ดแอปพลิเคชันของคุณ คุณต้องขออ็อบเจ็กต์ประเภท DataSource จากบริการตั้งชื่อและไดเร็กทอรีเท่านั้น ในกรณีนี้ รายละเอียดของการเชื่อมต่อและรหัสโปรแกรมที่ขึ้นอยู่กับการใช้งานเฉพาะของไดรเวอร์จะถูกซ่อนจากแอปพลิเคชันในวัตถุที่จัดเก็บไว้ในชื่อและบริการไดเร็กทอรี

ดังนั้น การแชร์อ็อบเจ็กต์ประเภท DataSource และ JNDI จึงเกี่ยวข้องกับสองขั้นตอนที่ดำเนินการโดยอิสระจากกัน:

  1. คุณต้องจัดเก็บวัตถุที่มีชื่อประเภท DataSource ในการตั้งชื่อและบริการไดเร็กทอรีโดยใช้Context.bind()ไฟล์javax.naming.
  2. ขอวัตถุประเภท DataSource จากบริการตั้งชื่อและไดเร็กทอรีในแอปพลิเคชันโดยใช้ไฟล์Context.lookup(). หลังจากนั้นคุณสามารถใช้วิธีการนี้DataSource.getConnection()เพื่อเชื่อมต่อกับฐานข้อมูลได้

ต่อไปนี้เป็นตัวอย่างของการใช้อินเทอร์เฟซ JNDI และอ็อบเจ็กต์ประเภท OracleDataSource ร่วมกัน

// Create a key JNDI object
Hashtable env = new Hashtable();
env.put (Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
env.put (Context.PROVIDER_URL, "file:JNDI");
Context ctx = new InitialContext(env);

// Get the DataSource object by its name
DataSource ods = (DataSource) ctx.lookup("myDatabase");

// Get the Connection from the DataSource object
Connection connection = ods.getConnection();
System.out.println("Connection successful!!!");

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

ไม่สำคัญว่ามันทำงานอย่างไร คุณเพียงแค่ต้องรู้ว่าด้วยบริการ JNDI คุณสามารถรับวัตถุพร็อกซีของบริการใดๆ ที่ลงทะเบียนในไดเร็กทอรีบริการได้ นี่คือวิธีที่คุณได้รับวัตถุ DataSource และใช้เพื่อทำงานกับฐานข้อมูล

7.4 ตัวอย่างการเชื่อมต่อพูล

กลับไปที่จุดเริ่มต้น - กลุ่มการเชื่อมต่อ

โปรแกรมเมอร์ Java ในโปรแกรมของพวกเขามักจะใช้ไลบรารีที่มีการใช้งานพูลการเชื่อมต่อที่หลากหลาย ในหมู่พวกเขามีสามยอดนิยม:

  • อาปาเช่คอมมอนส์ DBCP
  • C3PO
  • ฮิคาริซีพี

โครงการ Apache เป็นโครงการแรกที่สร้างกลุ่มการเชื่อมต่อที่ดี นอกจากนี้ยังใช้ภายในเว็บเซิร์ฟเวอร์ Tomcat ตัวอย่างของการทำงานร่วมกับมัน:

public class DBCPDataSource {

    private static BasicDataSource ds = new BasicDataSource();
    static {
    	ds.setUrl("jdbc:h2:mem:test");
    	ds.setUsername("user");
    	ds.setPassword("password");
    	ds.setMinIdle(5);
    	ds.setMaxIdle(10);
    	ds.setMaxOpenPreparedStatements(100);
    }

    public static Connection getConnection() throws SQLException {
    	return ds.getConnection();
    }

    private DBCPDataSource(){ }
}

พูลยอดนิยมอันดับสองคือC3PO ชื่อแปลกฉันเห็นด้วย ชื่อ C3PO อ้างอิงถึงหุ่นยนต์ c3po จาก Star Wars และ CP ก็ย่อมาจาก Connection Pool

public class C3p0DataSource {

    private static ComboPooledDataSource cpds = new ComboPooledDataSource();
    static {
    	try {
        	cpds.setDriverClass("org.h2.Driver");
        	cpds.setJdbcUrl("jdbc:h2:mem:test");
        	cpds.setUser("user");
        	cpds.setPassword("password");
    	} catch (PropertyVetoException e) {
            // handle the exception
    	}
    }

    public static Connection getConnection() throws SQLException {
    	return cpds.getConnection();
    }

    private C3p0DataSource(){}
}

เอกสารประกอบสามารถพบได้ในหน้าทางการ .

และสุดท้าย Connection Pool ที่ได้รับความนิยมสูงสุดในยุคของเราคือHikariCP :

public class HikariCPDataSource {

    private static HikariConfig config = new HikariConfig();
    private static HikariDataSource ds;

    static {
    	config.setJdbcUrl("jdbc:h2:mem:test");
    	config.setUsername("user");
    	config.setPassword("password");
    	config.addDataSourceProperty("cachePrepStmts", "true");
    	config.addDataSourceProperty("prepStmtCacheSize", "250");
    	config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
    	ds = new HikariDataSource(config);
    }

    public static Connection getConnection() throws SQLException {
    	return ds.getConnection();
    }

    private HikariCPDataSource(){}
}

นี่คือ หน้าGitHub อย่างเป็น ทางการ ของเขา

และบทความที่ดีเกี่ยวกับการกำหนดค่า