7.1 Pula połączeń

Dziś nauczymy się pracować z bazą danych jeszcze bardziej profesjonalnie. A teraz porozmawiamy o puli wątków lub puli połączeń w języku angielskim.

Połączenie z bazą danych zajmuje trochę czasu. Zwłaszcza jeśli baza danych jest zdalna. Jeśli wykonamy połączenie z bazą danych dla każdego żądania, wówczas odpowiedź naszej aplikacji będzie niewiarygodnie wolna. Nie wspominając o zasobach, które zużyje.

Jako rozwiązanie takich problemów zaproponowano przechowywanie połączeń z bazą w jakimś zbiorze, który nazywa się pulą wątków.

Kiedy żądamy nowego połączenia, pula połączeń tworzy je, a po zamknięciu nie zamyka go, ale zapisuje w puli połączeń. A jeśli ponownie poprosimy o połączenie z puli połączeń, da nam jedno ze starych zamiast tworzyć nowe.

W rzeczywistości aplikacja działa poprzez specjalny sterownik, który jest opakowaniem dla zwykłego sterownika JDBC i który posiada dodatkową funkcjonalność do pracy z pulą. Sytuację tę można przedstawić w następujący sposób:

pula połączeń

Programista może ręcznie ustawić ustawienia puli połączeń: ilość aktywnych połączeń, limit czasu itp.

W szczególnie ekstremalnych sytuacjach możesz napisać własną pulę połączeń: klasę, która będzie miała listę połączeń. Zastąpi funkcję zamykania, która przywróci połączenie z powrotem do listy, i będzie wiele innych gadżetów, takich jak zegar otwartego połączenia. Gdy nie ma połączenia przez dłuższy czas, połączenie jest zamykane.

7.2* Interfejs DataSource i ConnectionPoolDataSource

Pula wątków zwykle działa w tandemie z obiektem DataSource. Obiekt ten można traktować jako abstrakcję zdalnej bazy danych. Sama nazwa Data Source może być dosłownie przetłumaczona jako Data Source. Co jest rodzajem aluzji.

Obiekty DataSource służą do uzyskania fizycznego połączenia z bazą danych i stanowią alternatywę dla DriverManager. Nie ma potrzeby rejestrowania sterownika JDBC. Wystarczy ustawić odpowiednie parametry, aby nawiązać połączenie iwykonaj metodę getConnection()..

Podczas tworzenia obiektu typu DataSource lokalnie (bezpośrednio w aplikacji) parametry połączenia ustawiane są odpowiednimi metodami dostarczonymi przez dostawcę sterownika JDBC. Metody te nie są definiowane przez interfejs DataSource, ponieważ parametry łączenia się z DBMS od różnych dostawców mogą różnić się zarówno typem, jak i liczbą (na przykład nie wszystkie DBMS wymagają określenia typu sterownika lub protokołu sieciowego).

Zatem podczas pracy z Oracle DBMS, aby skorzystać z odpowiednich metod set i get, konieczne jest pozyskanie obiektu typu OracleDataSource, który jest instancją klasy o tej samej nazwie, która implementuje interfejs DataSource. Dlatego taki sposób tworzenia obiektów typu DataSource sprawia, że ​​Twój kod jest mniej przenośny i mniej zależny od konkretnej implementacji sterownika.

Poniżej znajduje się przykład kodu ilustrujący lokalne użycie obiektów typu 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* Interfejs JNDI

Pełne możliwości obiektów typu DataSource manifestują się w połączeniu z wykorzystaniem interfejsu JNDI. JNDI to specjalna usługa (coś w rodzaju katalogu) dla dużych aplikacji serwerowych, która pozwala jednej usłudze nawiązać połączenie z inną.

Korzystanie z nazwy i usługi katalogowej umożliwia przechowywanie obiektów typu DataSource utworzonych wcześniej przez administratora systemu z predefiniowanymi parametrami połączenia. Poniżej przedstawiono niektóre ze standardowych nazw właściwości (parametrów) opracowanych przez firmę Sun:

Nazwa właściwości Typ danych Javy Zamiar
nazwa bazy danych Strunowy Nazwa bazy danych
Nazwa serwera Strunowy Nazwa serwera
użytkownik Strunowy Nazwa użytkownika / ID)
hasło Strunowy Hasło użytkownika
numer portu int Numer portu serwera DBMS

Połączenie interfejsów DataSource i JNDI odgrywa kluczową rolę w rozwoju wielowarstwowych aplikacji korporacyjnych opartych na technologii komponentowej J2EE.

Jeśli w kodzie aplikacji używasz kombinacji interfejsów DataSource i JNDI, wystarczy zażądać obiektu typu DataSource z usługi nazewnictwa i katalogu. W takim przypadku szczegóły połączenia oraz kod programu zależny od konkretnej implementacji sterownika są ukrywane przed aplikacją w obiekcie przechowywanym w nazwie i usłudze katalogowej.

Zatem udostępnianie obiektów typu DataSource i JNDI obejmuje dwa kroki, które są wykonywane niezależnie od siebie:

  1. Nazwany obiekt typu DataSource należy przechowywać w usłudze nazewnictwa i katalogowej przy użyciu Context.bind()metody javax.naming.
  2. Zażądaj obiektu typu DataSource z usługi nazewnictwa i katalogu w aplikacji przy użyciu metody Context.lookup(). Następnie możesz użyć jego metody, DataSource.getConnection()aby uzyskać połączenie z bazą danych.

Poniżej przedstawiono przykład jednoczesnego użycia interfejsu JNDI i obiektu typu 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!!!");

Nie będziemy analizować pracy JNDI. To wykracza poza zakres tego kursu. Chcę tylko, żebyś wiedział, że to podejście jest bardzo powszechne w dużych aplikacjach. Nie zgub się, jeśli zobaczysz taki kod w przyszłości.

Nie ma znaczenia, jak to działa. Musisz tylko wiedzieć, że dzięki usłudze JNDI możesz uzyskać obiekt proxy dowolnej usługi zarejestrowanej w katalogu usług. W ten sposób otrzymujesz obiekt DataSource i używasz go do pracy z bazą danych.

7.4 Przykłady puli połączeń

Wróćmy do miejsca, w którym zaczęliśmy — pul połączeń.

Programiści Java w swoich programach bardzo często wykorzystują biblioteki, które zawierają różne implementacje pul połączeń. Wśród nich są trzy najpopularniejsze:

  • DBCP Apache Commons
  • C3PO
  • HikariCP

Projekt Apache był pierwszym, który stworzył dobrą pulę połączeń, jest również używany wewnątrz serwera WWW Tomcat. Przykład pracy z nim:

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

Drugą bardzo popularną pulą jest C3PO . Dziwne nazwy, zgadzam się. Nazwa C3PO nawiązuje do robota c3po z Gwiezdnych Wojen. A także CP jest skrótem od puli połączeń.

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

Dokumentację do niego można znaleźć na oficjalnej stronie .

I wreszcie najpopularniejszą pulą połączeń w naszych czasach jest 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(){}
}

Oto jego oficjalna strona GitHub .

I dobry artykuł o konfiguracji.