7.1 Verbindungspool

Heute lernen wir, wie wir noch professioneller mit der Datenbank arbeiten können. Und jetzt sprechen wir über den Thread-Pool oder Verbindungspool auf Englisch.

Das Herstellen einer Verbindung zur Datenbank dauert einige Zeit. Vor allem, wenn die Datenbank entfernt ist. Wenn wir für jede Anfrage eine Verbindung zur Datenbank herstellen, ist die Antwort unserer Anwendung unglaublich langsam. Ganz zu schweigen von den Ressourcen, die es verbrauchen wird.

Als Lösung für solche Probleme wurde vorgeschlagen, Verbindungen zur Basis in einem Satz zu speichern, der als Thread-Pool bezeichnet wird.

Wenn wir eine neue Verbindung anfordern, erstellt der Verbindungspool diese. Wenn sie geschlossen wird, wird sie nicht geschlossen, sondern im Verbindungspool gespeichert. Und wenn wir erneut eine Verbindung aus dem Verbindungspool anfordern, erhalten wir eine der alten, anstatt eine neue zu erstellen.

Tatsächlich arbeitet die Anwendung über einen speziellen Treiber, der einen Wrapper für einen regulären JDBC-Treiber darstellt und über zusätzliche Funktionen für die Arbeit mit dem Pool verfügt. Diese Situation lässt sich wie folgt darstellen:

Verbindungspool

Der Programmierer kann die Einstellungen für den Verbindungspool manuell festlegen: die Anzahl der aktiven Verbindungen, das Timeout usw.

Für besonders extreme Situationen können Sie Ihren eigenen Verbindungspool schreiben: eine Klasse, die eine Liste von Verbindungen enthält. Dadurch wird die Funktion „Schließen“ außer Kraft gesetzt, wodurch die Verbindung wieder in die Liste aufgenommen wird. Darüber hinaus gibt es viele weitere Extras wie den Timer für das Öffnen der Verbindung. Wenn längere Zeit keine Verbindung besteht, wird die Verbindung geschlossen.

7.2* Schnittstelle DataSource und ConnectionPoolDataSource

Der Thread-Pool arbeitet normalerweise zusammen mit dem DataSource-Objekt. Dieses Objekt kann als Abstraktion einer entfernten Datenbank betrachtet werden. Der Name „Datenquelle“ kann wörtlich mit „Datenquelle“ übersetzt werden. Das ist irgendwie ein Hinweis.

DataSource-Objekte werden verwendet, um eine physische Verbindung zu einer Datenbank herzustellen und sind eine Alternative zu DriverManager. Es ist nicht erforderlich, den JDBC-Treiber zu registrieren. Sie müssen lediglich die entsprechenden Parameter einstellen, um eine Verbindung herzustellen undFühren Sie die Methode getConnection() aus.

Beim lokalen Erstellen eines Objekts vom Typ DataSource (direkt in der Anwendung) werden die Verbindungsparameter durch die entsprechenden Methoden festgelegt, die vom JDBC-Treiberanbieter bereitgestellt werden. Diese Methoden werden nicht durch die DataSource-Schnittstelle definiert, da sich die Parameter für die Verbindung zu DBMS verschiedener Anbieter sowohl in der Art als auch in der Anzahl unterscheiden können (z. B. erfordern nicht alle DBMS die Angabe des Treibertyps oder des Netzwerkprotokolls).

Wenn Sie also mit dem Oracle DBMS arbeiten, ist es zur Verwendung der entsprechenden Set- und Get-Methoden erforderlich, ein Objekt vom Typ OracleDataSource abzurufen, bei dem es sich um eine Instanz der gleichnamigen Klasse handelt, die die DataSource-Schnittstelle implementiert. Daher ist Ihr Code durch diese Art der Erstellung von Objekten vom Typ „DataSource“ weniger portierbar und weniger abhängig von der spezifischen Implementierung des Treibers.

Im Folgenden finden Sie ein Codebeispiel, das die lokale Verwendung von Objekten vom Typ DataSource veranschaulicht.

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-Schnittstelle

Die volle Leistungsfähigkeit von Objekten vom Typ DataSource entfaltet sich in Kombination mit der Nutzung der JNDI-Schnittstelle. JNDI ist ein spezieller Dienst (so etwas wie ein Verzeichnis) für große Serveranwendungen, der es einem Dienst ermöglicht, eine Verbindung mit einem anderen herzustellen.

Mithilfe des Namens- und Verzeichnisdienstes können Sie zuvor vom Systemadministrator erstellte Objekte des Typs DataSource mit vordefinierten Verbindungsparametern speichern. Im Folgenden sind einige der von Sun entwickelten Standardnamen für Eigenschaften (Parameter) aufgeführt:

Name des Anwesens Java-Datentyp Zweck
Name der Datenbank Zeichenfolge Name der Datenbank
Servername Zeichenfolge Servername
Benutzer Zeichenfolge Benutzername / ID)
Passwort Zeichenfolge Benutzer-Passwort
Port-Nummer int Portnummer des DBMS-Servers

Die Kombination von DataSource- und JNDI-Schnittstellen spielt eine Schlüsselrolle bei der Entwicklung mehrschichtiger Unternehmensanwendungen auf Basis der J2EE-Komponententechnologie.

Wenn Sie in Ihrem Anwendungscode eine Kombination der DataSource- und JNDI-Schnittstellen verwenden, müssen Sie lediglich ein Objekt vom Typ DataSource beim Namens- und Verzeichnisdienst anfordern. In diesem Fall werden die Verbindungsdetails und der Programmcode, der von einer bestimmten Implementierung des Treibers abhängt, vor der Anwendung in dem im Namen und Verzeichnisdienst gespeicherten Objekt verborgen.

Somit umfasst die gemeinsame Nutzung von Objekten vom Typ DataSource und JNDI zwei Schritte, die unabhängig voneinander durchgeführt werden:

  1. Sie müssen das benannte Objekt vom Typ DataSource im Namens- und Verzeichnisdienst mithilfe Context.bind()der . speichern javax.naming.
  2. Fordern Sie mithilfe der . ein Objekt vom Typ DataSource vom Namens- und Verzeichnisdienst in der Anwendung an Context.lookup(). Anschließend können Sie mit dessen Methode DataSource.getConnection()eine Verbindung zur Datenbank herstellen.

Im Folgenden finden Sie ein Beispiel für die gemeinsame Verwendung der JNDI-Schnittstelle und eines Objekts vom Typ 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!!!");

Wir werden die Arbeit von JNDI nicht analysieren. Dies liegt außerhalb des Rahmens dieses Kurses. Ich möchte Sie nur darauf hinweisen, dass dieser Ansatz in großen Anwendungen sehr verbreitet ist. Verlieren Sie sich nicht, wenn Sie in Zukunft Code wie diesen sehen.

Es spielt keine Rolle, wie es funktioniert. Sie müssen nur wissen, dass Sie mit einem JNDI-Dienst das Proxy-Objekt jedes im Dienstverzeichnis registrierten Dienstes erhalten können. Auf diese Weise erhalten Sie das DataSource-Objekt und verwenden es für die Arbeit mit der Datenbank.

7.4 Beispiele für Verbindungspools

Kehren wir zu unserem Ausgangspunkt zurück – den Verbindungspools.

Java-Programmierer verwenden in ihren Programmen sehr häufig Bibliotheken, die verschiedene Implementierungen von Verbindungspools enthalten. Unter ihnen sind drei am beliebtesten:

  • Apache Commons DBCP
  • C3PO
  • HikariCP

Das Apache-Projekt war das erste, das einen guten Verbindungspool erstellt hat, er wird auch innerhalb des Tomcat-Webservers verwendet. Ein Beispiel für die Arbeit damit:

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

Der zweite sehr beliebte Pool ist C3PO . Seltsame Namen, da stimme ich zu. Der Name C3PO ist eine Anspielung auf den Roboter c3po aus Star Wars. Und CP ist auch die Abkürzung für 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(){}
}

Die Dokumentation dazu finden Sie auf der offiziellen Seite .

Und schließlich ist HikariCP der beliebteste Verbindungspool unserer Zeit :

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

Hier ist seine offizielle GitHub- Seite .

Und ein guter Artikel zur Konfiguration.