7.1 Pool de conexiuni

Astăzi vom învăța cum să lucrăm cu baza de date și mai profesional. Și acum vom vorbi despre pool-ul de fire, sau pool-ul de conexiuni în engleză.

Conectarea la baza de date durează ceva timp. Mai ales dacă baza de date este la distanță. Dacă facem o conexiune la baza de date pentru fiecare solicitare, atunci răspunsul aplicației noastre va fi incredibil de lent ca viteză. Ca să nu mai vorbim de resursele pe care le va consuma.

Ca o soluție la astfel de probleme, s-a propus stocarea conexiunilor la bază într-un set, care se numește pool de fire.

Când solicităm o nouă conexiune, pool-ul de conexiuni îl creează, iar când este închis, nu îl închide, ci îl salvează în pool-ul de conexiuni. Și dacă solicităm din nou o conexiune de la pool-ul de conexiuni, ne va oferi una dintre cele vechi în loc să creăm una nouă.

De fapt, aplicația funcționează printr-un driver special, care este un wrapper pentru un driver JDBC obișnuit și care are funcționalități suplimentare pentru lucrul cu pool-ul. Această situație poate fi reprezentată astfel:

bazin de conexiune

Programatorul poate seta manual setările pentru pool-ul de conexiuni: numărul de conexiuni active, timeout-ul etc.

Pentru situații deosebit de extreme, puteți scrie propriul pool de conexiuni: o clasă care va avea o listă de conexiuni. Va suprascrie funcția de închidere, care va întoarce conexiunea înapoi în listă și vor exista multe alte bunătăți, cum ar fi cronometrul conexiunii deschise. Când nu există nicio conexiune pentru o perioadă lungă de timp, conexiunea este închisă.

7.2* Interfață DataSource și ConnectionPoolDataSource

Pool-ul de fire funcționează de obicei în tandem cu obiectul DataSource. Acest obiect poate fi gândit ca o abstractizare a unei baze de date la distanță. Însuși numele Sursă de date poate fi tradus literal ca Sursă de date. Ceea ce este un fel de aluzie.

Obiectele DataSource sunt folosite pentru a obține o conexiune fizică la o bază de date și sunt o alternativă la DriverManager. Nu este nevoie să înregistrați driverul JDBC. Trebuie doar să setați parametrii corespunzători pentru a stabili o conexiune șiexecutați metoda getConnection()..

Când se creează un obiect de tip DataSource local (direct în aplicație), parametrii de conexiune sunt setați prin metodele adecvate furnizate de furnizorul de driver JDBC. Aceste metode nu sunt definite de interfața DataSource, deoarece parametrii pentru conectarea la DBMS de la diferiți furnizori pot diferi atât ca tip, cât și ca număr (de exemplu, nu toate DBMS-urile necesită specificarea tipului de driver sau a protocolului de rețea).

Astfel, atunci când lucrați cu SGBD-ul Oracle, pentru a utiliza metodele set și get corespunzătoare, este necesar să obțineți un obiect de tip OracleDataSource, care este o instanță a clasei cu același nume care implementează interfața DataSource. Prin urmare, acest mod de a crea obiecte de tip DataSource face codul mai puțin portabil și mai puțin dependent de implementarea specifică a driverului.

Următorul este un exemplu de cod care ilustrează utilizarea locală a obiectelor de tip 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* Interfață JNDI

Capacitățile complete ale obiectelor de tip DataSource se manifestă în combinație cu utilizarea interfeței JNDI. JNDI este un serviciu special (ceva ca un director) pentru aplicații de server mari care permite unui serviciu să stabilească o conexiune cu altul.

Utilizarea serviciului de nume și director vă permite să stocați obiecte de tip DataSource create anterior de administratorul de sistem cu parametri de conexiune predefiniti. Următoarele sunt câteva dintre numele de proprietăți (parametri) standard dezvoltate de Sun:

Numele proprietatii tip de date Java Scop
numele bazei de date Şir Numele bazei de date
numele serverului Şir Numele serverului
utilizator Şir Nume de utilizator (ID)
parola Şir Parolă de utilizator
numarul portului int Numărul portului serverului DBMS

Combinația de interfețe DataSource și JNDI joacă un rol cheie în dezvoltarea aplicațiilor de întreprindere multi-nivel bazate pe tehnologia componentelor J2EE.

Dacă utilizați o combinație de interfețe DataSource și JNDI în codul aplicației, trebuie doar să solicitați un obiect de tip DataSource de la serviciul de denumire și director. În acest caz, detaliile conexiunii și codul programului dependent de o anumită implementare a driverului sunt ascunse de aplicație în obiectul stocat în serviciul de nume și director.

Astfel, partajarea obiectelor de tip DataSource și JNDI implică doi pași care se efectuează independent unul de celălalt:

  1. Trebuie să stocați obiectul numit de tip DataSource în serviciul de denumire și director Context.bind()folosind javax.naming.
  2. Solicitați un obiect de tip DataSource de la serviciul de denumire și director din aplicație folosind Context.lookup(). După aceea, puteți folosi metoda sa DataSource.getConnection()pentru a obține o conexiune la baza de date.

Următorul este un exemplu de utilizare împreună a interfeței JNDI și a unui obiect de tip 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!!!");

Nu vom analiza activitatea JNDI. Acest lucru este în afara domeniului de aplicare al acestui curs. Vreau doar să știți că această abordare este foarte comună în aplicațiile mari. Nu vă pierdeți dacă vedeți un astfel de cod în viitor.

Nu contează cum funcționează. Trebuie doar să știți că cu un serviciu JNDI puteți obține obiectul proxy al oricărui serviciu înregistrat în directorul de servicii. Acesta este modul în care obțineți obiectul DataSource și îl utilizați pentru a lucra cu baza de date.

7.4 Exemple de pool de conexiuni

Să revenim la locul de unde am început - pool-uri de conexiuni.

Programatorii Java în programele lor folosesc foarte des biblioteci care conțin diverse implementări ale pool-urilor de conexiuni. Printre acestea sunt trei cele mai populare:

  • Apache Commons DBCP
  • C3PO
  • HikariCP

Proiectul Apache a fost primul care a creat un pool de conexiuni bun, este folosit și în interiorul serverului web Tomcat. Un exemplu de lucru cu el:

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(){ }
}

Al doilea bazin foarte popular este C3PO . Nume ciudate, sunt de acord. Numele C3PO este o referire la robotul c3po din Star Wars. Și, de asemenea, CP este prescurtarea pentru 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(){}
}

Documentația pentru aceasta poate fi găsită pe pagina oficială .

Și, în sfârșit, cel mai popular Pool de conexiune din vremea noastră este 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(){}
}

Iată pagina lui oficială GitHub .

Și un articol bun despre configurare.