SQL-Tutorial Teil 2 - JDBC (Java Database Connectivity)

Teil 2 des SQL-Tutorials beschreibt die Verwendung der JDBC-API zum Erstellen eines Java-Clients für eine MySQL-Datenbank. Zunächst werden die grundlegenden JDBC-Funktionen an einem Mini-Beispiel "Datenabfrage mit JDBC" vorgestellt, das den Inhalt einer einzigen Tabelle abfragt. Abschließend folgt ein größeres Beispiel "Datenbank-Client mit JDBC", das einen Connection Pool für die effiziente Verwaltung offener Datenbankverbindungen verwendet. Zudem werden per Interface einheitliche Methoden zum Abfragen und Bearbeiten der Datenbanktabellen implementiert. Als Entwicklungsumgebung für den Java Client wird Visual Studio Code verwendet, und für die Projektverwaltung das Build-Management-Tool Maven. Als Vorbereitung sollte die Beispiel-Datenbank "University" CreateDatabaseUniversity(MySQL).sql mit dem hier verlinkten Skript in einer MySQL-Datenbank angelegt werden, wie im SQL-Tutorial beschrieben.

  Motivation

  Java ist für die Entwicklung von Datenbank-Clients besonders gut geeignet, da sie eine objektorientierte Programmierung erzwingt, die gut auf die Tabellenstruktur relationaler Datenbanken abgebildet werden kann.
  Java Database Connectivity (JDBC) ist die zentrale Java-API, mittels deren Hilfe man in Java einen Datenbank-Client erstellt. JDBC ist standardisiert, die aktuelle Version der JDBC-API ist 4.3. Jeder Datenbank-Hersteller muss die Spezifikation der JDBC-API einhalten, dadurch wird der Zugriff auf verschiedene Datenbanken unabhängig vom Hersteller ermöglicht.

Visual Studio Code wird aktuell vor allem für Python-Programmierung eingesetzt, verfügt jedoch über Extensions (Extension Pack for Java), die den Einstieg in die Java-Programmierung erleichtern. Wer in mehreren Sprachen programmiert und Visual Studio Code schon für Python- oder PHP-Programmierung einsetzt, kann direkt in Visual Studio Code auch Java programmieren.


    Top

1 Datenbank-Clients entwickeln

Datenbanken werden verwendet, um als Datenspeicher für Web- und Desktop-Anwendungen zu dienen. Für den Datenbankzugriff aus einem Client bietet jede Programmiersprache spezielle API's an.
  In Java:
JDBC-API: https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/
Die JDBC-API (Java Database Connectivity API) stellt eine standardisierte Funktionalität zur Verfügung und enthält lediglich Interfaces.
Die konkreten Implementierungen findet man in den JDBC-Treibern der verschiedenen DBMS-Anbieter.
  In PHP:
MySQLi-Extension: https://www.php.net/manual/en/book.mysqli.php
Funktioniert nur mit MySQL, kein Datenbanktreiber erforderlich
PHP Data Objects (PDO): https://www.php.net/manual/en/intro.pdo.php
Funktioniert mit allen Datenbankservern, Datenbanktreiber erforderlich.

Java ist eine objektorientierte Programmiersprache, die vor allem für Desktop-Anwendungen in sicherheitskritischen Bereichen eingesetzt wird, z.B. im Bankenumfeld. Während bei Webanwendungen im Internet vor allem PHP- und JavaScript-basierte Frameworks zu finden sind, wird Java eher für unternehmensinterne Webanwendungen verwendet, bei denen die Daten in einer Datenbank erfasst werden und die eine Sitzungsverwaltung verwenden.

Die JDBC-API stellt Funktionalität zur Verfügung, mit der man in Java-Anwendungen die Verbindung zur Datenbank herstellen kann, SQL-Abfragen an den Datenbankserver sendet, die zurückerhaltenen Ergebnisse ausliest, und die Datenbankverbindung wieder schließt. Sie bildet weiterhin die Datentypen der Datenbanken auf Datentypen der Programmiersprache ab.

    Top

2 JDBC API: Übersicht

JDBC (Java Database Connectivity) ist eine Java-Bibliothek, die eine einheitliche Schnittstelle zu relationalen Datenbanken verschiedener Hersteller bietet. Die JDBC API besteht aus den packages java.sql, javax.sql, die folgende Interfaces und Klassen enthalten:

  • DriverManager: verwaltet JDBC-Treiber.
      Wichtige Methoden: getConnection()
  • Connection: erstellt Verbindung mit einer Datenbank
      Wichtige Methoden: createStatement(), prepareStatement(), close(), commit(), rollback()
  • Statement: Statische Datenabfrage
      Wichtige Methoden: executeQuery(), executeUpdate()
  • PreparedStatement: Dynamische parametrisierte Datenbankabfrage
      Wichtige Methoden: executeUpdate(), executeBatch()
  • ResultSet: kapselt das Ergebnis der Datenbankabfrage
      Wichtige Methoden: first(), last(), next(), getInt(), getString(), updateRow(), close()

Das UML-Klassendiagramm zeigt einen Auszug der wichtigsten Klassen der JDBC-API mit den jeweiligen Abhängigkeiten. Es wird nur eine Auswahl der Methoden / Funktionen einer Klasse gezeigt, die vollständige Übersicht findet man in der Dokumentation der JDBC-API. Jede der Methoden kann im Fehlerfall eine SQLException auslösen, erkennbar an der Signatur der Methoden. Die vollständige Signatur der Methode getConnection() ist z.B.

Connection java.sql.DriverManager.getConnection(String url, String user, String password) throws SQLException

Dies bedeutet, dass die Methode in einen Try-Block eingebettet werden muss, damit der Fehler im Catch-Block angemessen abgefangen werden kann. Alternativ kann die SQLException mit einem throw an die aufrufende Funktion weitergereicht werden, die ihrerseits dann für die Fehlerbehandlung zuständig ist.

try {
  Connection conn = DriverManager.getConnection(url, user, passwd);
}
catch (SQLException e){
   // Hier Fehlerbehandlung, mindestens per Logging
}

JDBC Übersicht
Die JDBC-API enthält lediglich die Interfaces, die die Signatur (Namen und Parameter) der Methoden / Funktionen festlegen. Die konkreten Implementierungen findet man in den JDBC-Treibern der verschiedenen DBMS-Anbieter, die als *.jar-Dateien verpackt auf den Webseiten des Anbieters zum Download angeboten werden. Z.B. findet man den MySQL-Treiber mysql-connector-java-9.00.jar auf der MySQL-Webseite, inkl. Dokumentation und Anleitungen. Da das manuelle Herunterladen von *.jar-Dateien umständlich ist, wird in Java-Projekten der automatische Download mittels des Projektmanagement-Tools Maven bevorzugt.
    Top

2-1 Datenbank mit JDBC abfragen

Der übliche Ablauf beim Abfragen einer Datenbank mit JDBC ist wie folgt.

Schritt 1 Verbindung aufbauen
Der Client baut eine Verbindung zum Datenbankserver (Datenbankmanagementsystem, DBMS) auf, den er anhand einer URL mit festgelegter Syntax identifiziert, und authentifiziert sich mit dem Benutzernamen und Passwort eines Users, der zuvor im DBMS angelegt wurde. Client und Server tauschen Meta-Informationen aus (Name des Protokolls, Version).

Schritt 2 Datenbankabfrage ausführen
Nach dem erfolgreichen Aufbau der Datenbankverbindung ("Connection") kann der Client SQL-Queries an den DBMS-Server senden. Der Server führt die Query aus, und sendet die Ergebnisliste an den Client zurück. Der Client prüft, ob die Ergebnisliste (ResultSet) nicht leer ist und liest die Zeilen in einer Schleife aus.

Schritt 3 Verbindung beenden
Die Verbindung muss nach Auführen der Datenabfrage geschlossen werden, da sonst unnötig Ressourcen belegt werden.

Beispiel: Datenbank mit JDBC abfragen
Die Methode testSelect() der Java-Klasse JDBCTest kapselt eine minimale Datenbankabfrage, die den Inhalt der Tabelle Student der University-Datenbank als Liste speichert und ausgibt. Das Java-Programm kann in Visual Studio Code ausgeführt werden, dafür muss zunächst ein Java-Projekt mit Maven angelegt werden, wie in Abschnitt 5-1 Maven Projekt erstellen beschrieben, und die Java-Klasse dort angelegt werden.

  JDBCTest.java

package com.hskl.dbs;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
record Student(String name, String vorname, int matrikelnr) {}
public class JDBCTest {
  final static String url = "jdbc:mysql://localhost:3306/university";
  final static String driver = "com.mysql.cj.jdbc.Driver";
  final static String login = "<user>";
  final static String password = "<password>";
  public static void main(String[] args) {
      try {
          testSelect();
      } catch (SQLException e) {
          e.printStackTrace();
      }     
  }
  public static void testSelect() throws SQLException {
    List<Student> stdList = new ArrayList<Student>();
    // (1) Datenbankverbindung herstellen
    Connection conn = DriverManager.getConnection(url, login, password);   
    try { // (2) Datenbankabfrage ausführen                  
        String query = "SELECT Name, Vorname, Matrikelnr FROM Student";
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery(query);       
        if (rs != null) { // Solange es Datensätze gibt
            while (rs.next()) { // Schleife über die Ergebnisliste 
              Student st = new Student( rs.getString("name"),
                           rs.getString("vorname"),
                           rs.getInt("matrikelnr"));
              stdList.add(st);                    
            }
        }
        System.out.println(stdList.toString());
    }
    finally {   
      conn.close();   // (3) Verbindung beenden
    }               
  }
}

  Erläuterung

    Der übliche Ablauf beim Abfragen einer Datenbank besteht im Herstellen der Datenbankverbindung, Ausführen der Datenabfrage und Schließen der Datenbankverbindung. Bei jedem der Schritte können Fehler auftreten (falsche Datenbank-URL, fehlende Berechtigung, Fehler in der SQL-Syntax). Die Fehler aus der Datenabfrage werden hier direkt in der Funktion testSelect() abgefangen, die Fehler aus dem Herstellen und Schließen der Datenbankverbindung werden mit throws von der Funktion testSelect() an die aufrufende Funktion weitergereicht.

  • (1) Datenbankverbindung herstellen
    Die Methode DriverManager.getConnection() benötigt als Parameter die URL der Datenbank sowie Login und Passwort des Datenbanknutzers.
    Verwendete JDBC-Klassen: Connection, DriverManager

  • (2) Datenbankabfrage ausführen
    Hier wird eine statische Datenbankabfrage ausgeführt. Zunächst wird die SQL-Query zusammengebaut, dann ein neues Statement erzeugt, dann die executeQuery-Methode des Statements aufgerufen und schließlich in einer Schleife die Datensätze ausgegeben.
    Verwendete Klassen: Statement (Datenbankabfrage) und ResultSet (Ergebnisliste).

  • (3) Datenbankverbindung schließen
    Das Schließen der Datenbankverbindung ist ein wesentlicher Schritt. Verbindungen, die offen bleiben, belegen auf dem Datenbankserver unnötig Ressourcen. Die Verbindung wird im finally-Teil eines try-finally-Blocks geschlossen.


Eine Datenbankverbindung sollten einerseits nach Durchführen der Abfragen geschlossen werden, da zu viele offene Verbindungen die Datenbank überlasten. Andererseits ist die Wiederverwendung offener Verbindungen wünschenswert, weil der Aufbau einer Datenbankverbindung-Verbindung eine zeitintensive Operation ist. Bei kleinen Desktop-Clients kann z.B. eine einmal geöffnete Datenbankverbindung für mehrere aufeinanderfolgende Abfragen verwendet werden und erst bei Schließen des Clients geschlossen werden. Web-Anwendungen mit vielen Abfragen, wo viele Clients gleichzeitig auf die Datenbank zugreifen, müssen für das Öffnen und Schließen von Datenbankverbindungen Connection Pools einsetzen, das sind Komponenten, die eine optimale Wiederverwendung der Datenbankverbindungen ermöglicht.

    Top

2-2 Connection Pools

Das Aufbauen und Schließen einer Datenbankverbindung ist ein aufwendiger Schritt, der durch Connection Pooling verbessert werden kann. Ein Connection Pool ist ein Framework, das die Wiederverwendung physischer Datenbankverbindungen ermöglicht und die Geschwindigkeit des Datenbankzugriffs und den Speicherverbrauch optimiert. In einem Connection Pool wird eine festgelegte Anzahl Verbindungen offengehalten und diese werden wiederverwendet. In Java ist Connection Pooling entweder Teil des JDBC-Treibers, oder eine separate Klassenbibliothek (HikariCP), oder wird als Teil eines Application Servers (JBoss, Tomcat) angeboten. JDBC 2.0 definiert das Interface ConnectionPoolDataSource, das eine Schnittstelle für die Erzeugung von Pooled Connections zur Verfügung stellt.

    Top

2-3 Abfragen per Statement oder Prepared Statement

Ein Prepared Statement ist eine dynamische parametrisierte Abfrage. Die Kommunikation mit dem Datenbankservern läuft in zwei Schritten ab: zunächst wird eine Datenabfrage mit Platzhaltern (z.B. ?) an den Server gesendet, danach nur noch die Werte, die an die Platzhalter gebunden werden.

Prepared Statements haben im Vergleich zu Statements zwei Vorteile, sie verbessern die Performance und Sicherheit der Datenbankabfrage.
Performance: Soll ein Statement mit unterschiedlichen Parametern wiederholt ausgeführt werden, sind Prepared Statements effizienter, da das Statement schon vorübersetzt am Server vorliegt und nur noch mit den neuen Parametern ausgeführt werden muss.
Sicherheit: Mittels Prepared Statement können Angriffe wie SQL Injection verhindert werden, da das Datenbanksystem die Gültigkeit von Parametern prüft, bevor diese verarbeitet werden.

3 Datenbanktreiber auswählen

    Top

Ein Datenbanktreiber ist eine Java-Bibliothek bzw. ein Programm-Paket (*.jar-Datei), das die konkrete Implementierung der in der JDBC-API spezifizierten Schnittstellen für ein bestimmtes Datenbankmanagementsystem (DBMS) enthält. Jeder DBMS-Hersteller bietet Java-Datenbanktreiber für alle gängigen Programmiersprachen an. Datenbanktreiber können entweder auf der Webseite des Herstellers als *.jar-Datei heruntergeladen werden, oder automatisiert durch die Verwendung des Build-Tools Maven als Projektbibliothek eingetragen werden, dann übernimmt Maven den Download.

Bei der Wahl des passenden Treibers muss auf Kompatibilität geachtet werden: die Version des Treibers muss zur Version des installierten DBMS und zur verwendeten Java-Version passen. Informationen dazu findet man auf der JDBC-Webseite des jeweiligen Herstellers.

3-1 JDBC-Treiber für SQL Server: MSSQL JDBC

Der JDBC-Treiber für SQL Server heißt MSSQL JDBC und implementiert die Java Database Connectivity API für SQL Server. Microsoft bietet auf der Webseite des SQL Server aktuelle Versionen des JDBC-Treibers zum Download an, inklusive Anleitungen. Die JDBC-Treiber heißen mssql-jdbc-12.4.1.jre11.jar oder so ähnlich, wobei am Namen zu erkennen ist, zu welcher Java-Version sie jeweils passen.

 Webseite des Microsoft SQL Server JDBC Treiber

3-2 JDBC-Treiber für MySQL: MySQL Connector-J

    Top

Der offizielle JDBC-Datenbanktreiber für MySQL ist MySQL Connector/J und wird auf der Webseite des DBMS-Herstellers zum Download angeboten, inklusive Anleitungen zur Verwendung. Die JDBC-Treiber heißen mysql-connector-java-8.0.13.jar oder so ähnlich, wobei am Namen die Version des Treibers zu erkennen ist, und zu welcher Java-Version er jeweils passt.

 Webseite Oracle MySQL Connector/J

Das Herunterladen der Jar-Datei in der passenden Version von der Webseite des Hersteller und Einbinden in den Java Bild-Path des Java-Projekts kann entweder manuell erfolgen, oder mit dem Projektverwaltungs-Tool Maven automatisiert werden. Bei der Verwendung von Maven werden die benötigten Java-Pakete in die zentrale Konfigurationsdatei pom.xml eingetragen und infolgedessen von Maven automatisch aus dem Maven Repository heruntergeladen.

Das Konzept des Datenbanktreibers ist nicht auf die Programmiersprache Java beschränkt, es gibt ähnliche APIs auch in anderen Programmiersprachen.

4 Java-Programmierung mit Visual Studio Code und Maven

    Top

Für die Entwicklung von Java-Programmen stehen Entwicklungsumgebungen wie IntelliJ IDEA und Eclipse zur Verfügung, die die benötigte Funktionalität für professionelle Software-Entwicklung mit Java einbinden. Für Einsteiger sind diese Entwicklungsumgebungen gerade wegen der Vielzahl an Funktionen oft zu mächtig, hier sind "leichte" Entwicklungsumgebungen wie Visual Studio Code nützlich.

4-1 Visual Code für Java konfigurieren

Visual Studio Code ist eine minimalistische Entwicklungsumgebung von Microsoft, die aktuell vor allem für Python-Programmierung eingesetzt wird. Visual Studio Code kann über verschiedene Extensions auch für Java-Programmierung eingesetzt werden.

Um Visual Studio Code für Java Programmierung zu verwenden, muss zunächst Microsoft's Extension Pack for Java installiert werden. Diese Extension enthält eine Reihe von Komponenten, die für die Java-Entwicklung wichtig sind: Language Support for Java™ by Red Hat, Debugger for Java, Test Runner for Java, Maven for Java, Project Manager for Java und Visual Studio IntelliCode.

Nach der Installation des Extension Packs für Java wird das Plugin unter den installierten Extensions angezeigt.

Visual Studio Code: Extension Pack for Java

4-2 Java Development Kit installieren

    Top

Für die Java-Programmierung wird ein aktuelles JDK (Java Development Kit) benötigt. Das JDK besteht aus der Java Runtime (JRE) und enthält auch den Source Code der Java Standardpakete, was zum Debuggen benötigt wird. Es stehen zwei JDKs zur Auswahl:
  1. Oracle JDK
ist für die kommerzielle Verwendung lizenzpflichtig, kann für den persönlichen Gebrauch kostenlos verwendet werden.
Aktuellere JDK-Versionen sind 21, 22 und 23. Für die Durchführung des Tutorials wird der Download der JDK-Version 21 als zip-Datei empfohlen, der Name ist jdk-21_windows-x64_bin.zip oder ähnlich.

  Oracle JDK Downloads www.oracle.com/java/technologies/downloads/


  2. OpenJDK
ist eine kostenlose Open Source-Version des Oracle JDKs und im wesentlichen identisch. OpenJDK wird von unterschiedlichen Organisationen bereitgestellt, wie z.B. RedHat oder Adoptium. Adoptium ist eine Community-Organisation, die kostenlose Java-Laufzeitumgebungen (JDK/JRE) zur Verfügung stellt.

Aktuellere OpenJDK-Versionen sind 21 und 25. Für die Durchführung des Tutorials wird der Download der OpenJDK-Version 21 als zip-Datei empfohlen, der Name ist OpenJDK21U-jdk_x64_windows_hotspot_21.0.9_10.zip oder ähnlich.

  OpenJDK Downloads: Adoptium Builds adoptium.net/de/temurin/releases

Nach dem Herunterladen und Extrahieren der Datei in einen passenden Ordner z.B. C:\Java\OpenJDK21U\jdk-21.0.9+10, sollte die System-Umgebungsvariable JAVA_HOME auf den Ordner gesetzt werden, dies erfolgt über Systemeinstellungen > "Systemumgebungsvariablen bearbeiten". Weiterhin sollte der Ordner C:\Java\OpenJDK21U\jdk-21.0.9+10\bin der Systemumgebungsvariablen PATH hinzugefügt werden, also dem Systempfad, damit die Java Programme gefunden werden.

Das Setzen der Umgebungsvariablen wird überprüft, indem man im Terminalfenster (cmd) folgende Befehle eingibt:

JDBC Übersicht

4-3 Java-Projekte mit Apache Maven verwalten

    Top

Apache Maven ist ein Build- und Projektverwaltungs-Tool, das für die automatisierte Entwicklung von Java-Projekten verwendet wird und den kompletten Lebenszyklus der Softwareentwicklung unterstützt: Installation, Kompilieren, Testen und Deployen von Java-Anwendungen. Maven kann auf zwei Arten verwendet werden: über Terminal-Kommandos wie mvn install, oder über die Menüpunkte einer Entwicklungsumgebung, in die Maven integriert ist. Einige Java-Entwicklungsumgebungen wie IntelliJ IDEA oder Eclipse haben Maven schon integriert. Bei Visual Studio Code wird Maven durch eine Extension eingebunden. Nach Installation der Extension hat man verschiedene Menüpunkte, um Maven-Projekte mit standardisierter Struktur zu erstellen und Maven Goals (install, compile, run) auszuführen. Ein Java-Projekt, das mit Maven erstellt wird, hat eine standardisierte Projekt-Struktur und eine zentrale Konfigurationsdatei pom.xml, in die alle Informationen zum Ausführen des Projektes eingetragen werden.

Man kann Projekte aus den Vorlagen des Maven Repository erstellen. Die Projektabhängigkeiten werden über eine Konfigurationsdatei pom.xml konfiguriert. Damit entfällt das Herunterladen und manuelle Einhängen der *.jar-Dateien im Projekt. Maven ist verfügbar in allen Java-IDEs: Eclipse, IntellijIdea, Visual Studio Code.

5 Datenbank-Client mit JDBC und Connection Pool

In diesem Beispiel wird ein Java-Client mittels JDBC erstellt, und zwar für eine University-Datenbank, die Studiengänge, Studenten und Module verwaltet, und auch im SQL-Tutorial auf dieser Webseite verwendet wird. Als Vorbereitung sollte man MySQL via XAMPP installieren und die Beispiel-Datenbank "University" per hier verlinktem Skript erstellen.
 CreateDatabaseUniversity(MySQL).sql

Der Java-Client für die University-Datenbank wird jede Datenbanktabelle mit einer entsprechenden Java-Klasse abbbilden, d.h. zur Tabelle Student gibt es eine Klasse Student mit denselben Attributen, und für jede Tabelle Methoden anbieten, mit denen man Datensätze einfügen, ändern, löschen und abfragen kann. Die Methoden für die Datenbankzugriffe werden in einer Java-Schnittstelle ("Interface") festgelegt, wir nennen sie: insert(), update(), delete() und findAll().

  UML-Klassendiagramm

UML-Klassendiagramm des JDBC-Clients

  Erläuterung des Diagramms

Das UML-Klassendiagramm beschreibt den Aufbau des Packages com.hskl.university.dataaccess, das die Klassen für den Java-Client mit JDBC enthält.

  • DataSource - Klasse, dient dem Herstellen einer Datenbankverbindung
  • DataAccess - Interface, legt die Methoden für die Datenbankzugriffe fest
  • StudentDataAccess - Subklasse von DataSource, implementiert das Interface DataAccess
  • Student - bildet die Datenbanktabelle Student als Klasse ab

5-1 Java-Projekt mit Maven erstellen

    Top

Die Erstellung eines neuen Java-Projektes mit Maven ist in der folgenden Slideshow beschrieben, die auszuwählenden Eingaben sind farblich hervorgehoben. Hier wird ein einfaches Maven-Projekt ohne Vorlage erstellt ("no archetype"), dabei ist zunächst nur die Projektstruktur und eine minimale Konfigurationsdatei pom.xml ohne Abhängigkeiten vorhanden.

  • Schritt 1: File > New > New Java Project ... (Project Manager for Java)
  • Schritt 2: Maven create from archetype (provided by Maven for Java)
  • Schritt 3: No archetype (create a basic Maven project directly)
  • Schritt 4: New Maven Project: Input group ID of your project: com.hskl.university.dataaccess
  • Schritt 5: New Maven Project: Input artifact ID (also project name): universityclient
  • Schritt 6: Auswählen eines Ordners im Dateisystem als Projektablage. Hier wird der zuvor erstellte Ordner Workspace ausgewählt. Das Projekt universityclient wird dann als Unterordner des Ordners Workspace abgelegt.
  • Schritt 7: Es erscheint eine Meldung, die die Erstellung des Projekts universityclient im Ordner Workspace bestätigt.

    Top

5-2 Konfigurationsdatei pom.xml anpassen

Das im vorigen Schritt erstellte Java-Projekt mit Maven wird für die Verwendung der JDBC-Treiber konfiguriert. Dafür werden in der Maven-Konfigurationsdatei pom.xml die benötigten Java-Bibliotheken als Abhängigkeiten ("dependencies") eingetragen. Für einen Datenbank-Client werden mindestens zwei Dependencies benötig: zunächst der JDBC-Treiber selber und zusätzlich eine Connection Pool-Implementierung, die die offenen Datenbankverbindungen verwaltet.

Die korrekten Angaben für einen Datenbanktreiber entnimmt man dem Maven Repository, https://mvnrepository.com/.
Das Maven Repository ist eine Online-Plattform, in der die Hersteller von Java-Paketen zentral die Angaben hinterlegen, die ein Java-Programmierer benötigt, um das jeweilige Paket in der eigenen Anwendung verwenden zu können. Man findet die Dependency-Einträge von Java-Bibliotheken im Maven Repository entweder über die Menüleiste, die verschiedene Kategorien zusammenfasst z.B. Logging Frameworks, Testing Frameworks oder JDBC Drivers) oder über die Suche.

  Konfigurationsdatei pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.hskl.university</groupId>
    <artifactId>universityclient</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
    </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>9.1.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>7.0.2</version>
        </dependency>
    </dependencies>
</project>

  Maven Repository

https://mvnrepository.com/artifact/com.mysql/mysql-connector-j
    Top

5-3 Datenbanktabellen auf Klassen abbilden

Jede Datenbanktabelle wird durch eine POJO (plain old java object)-Klasse abgebildet, die lediglich die Attribute / Spalten der Tabelle, einen Klassen-Kostruktor, öffentliche Getter-und Setter-Methoden für die Attribute, und eine toString-Methode enthält.
Die Tabelle Studiengang wird durch die Klasse Studiengang abgebildet, mit den Attributen ID, name und art.
Die Tabelle Student wird durch eine Klasse Student abgebildet, mit den Attributen ID, name, vorname, matrikelnr, semester und studiengang.

  POJO-Klasse Studiengang

package com.hskl.university.dataaccess;
public class Studiengang {  
  int ID = 1;
  String name = "";  
  String art = "";  
  public Studiengang(int ID){
    this.ID = ID;
  }
  public Studiengang(int iD, String name, String art) {
    this.ID = iD; this.name = name; this.art = art;
  }
  public int getID() {
    return ID;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
        this.name = name;
  }
  public String getArt() {
    return art;
  }
  public void setArt(String art) {
        this.art = art;
  }
  @Override
  public String toString() {
    return "Studiengang [name=" + name + ", art=" + art + "]\n";
  }
}

  POJO-Klasse Student

package com.hskl.university.dataaccess;
public class Student {
  int ID = 0;
  String name;
  String vorname;
  int matrikelnr = 0;
  int semester = 0;
  Studiengang studiengang = new Studiengang(1, "", "");
  
  public Student(String name, String vorname, int matrikelnr) {
    this.name = name;
    this.vorname = vorname;
    this.matrikelnr = matrikelnr;
  }
  public String getName() {
        return name;
    }
  public void setName(String name) {
   this.name = name;
  }
  // Weitere Getter und Setter
    @Override
  public String toString() {
    return String.format(" %10s %10s %10d\n", 
    this.name, this.vorname, this.matrikelnr);
  }
}

Mit Hilfe der POJO-Klassen kann man neue Studiengang- und Student-Instanzen erstellen, wie in dem folgenden Beispiel.

Studiengänge und Studenten erstellen

Studiengang stg = new Studiengang(1, "Elektrotechnik", "Master");
Student anna = new Student("Test", "Anna", 11222);
anna.setStudiengang(stg);
System.out.println(anna.toString());
    Top

5-4 Datenbankverbindung herstellen: Klasse DataSource

Die Klasse DataSource dient dem Erstellen einer Verbindung zur Datenbank. Hier werden die Verbindungsdaten festgelegt: URL, Benutzername und Passwort, und es wird eine statische Instanz des HikariCP ConnectionPools erstellt.

  Klasse DataSource

package com.hskl.university.dataaccess;
import java.sql.Connection;
import java.sql.SQLException;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
public class DataSource {
  private final static String url = "jdbc:mysql://localhost:3306/university"; 
  private final static String login ="student01"; 
  private final static String password = "dbs";
  private static HikariDataSource ds = null;
  static {
    HikariConfig config = new HikariConfig();
    config.setJdbcUrl(url);
    config.setUsername(login);
    config.setPassword(password);
    config.addDataSourceProperty("minimumIdle", "5");
    config.addDataSourceProperty("maximumPoolSize", "25");
    ds = new HikariDataSource(config);
  }
  public static Connection getConnection() throws SQLException {    
    return ds.getConnection();
  }
  public static void closeConnection() throws SQLException {
    ds.getConnection().close();
  }
}

  Klasse DataSource testen

package com.hskl.university.dataaccess;
import java.sql.Connection;
import org.junit.Test;
public class DataSourceTest {
  @Test
  public void testGetConnection() throws Exception {
    Connection conn = DataSource.getConnection();
    try {           
        System.out.println("Datenbankverbindung offen!");
        System.out.println("Datenbanksystem: " 
              + conn.getMetaData().getDatabaseProductName());
    }
    finally {         
        conn.close();
        System.out.println("Datenbankverbindung geschlossen!");
    }
  }
}

5-5 Datenbankzugriffe festlegen: Interface DataAccess

    Top

Das Interface DataAccess legt die zulässigen Datenbankzugriffe fest, also welche Methoden zum Einfügen, Ändern, Löschen und Auslesen der Datenbanktabellen im Client benötigt werden. Anschließend wird für jede Datenbanktabelle eine Realisierungs-Klasse für das Interface geschrieben, die die konkrete Implementierung der festgelegten Methoden enthält.

Interface DataAccess

package com.hskl.university.dataaccess;
import java.util.List;
public interface IDataAccess<T> {
  int insert(T obj) throws SQLException;
  int update(T obj) throws SQLException;
  int delete(T obj) throws SQLException;
  void insertAll(List<T> list) throws SQLException;
  List<T> findAll() throws SQLException;
  List<T> find(T filter) throws SQLException;
}
    Top

5-6 Datenbankzugriffe implementieren: StudentDataAccess

Die Klasse StudentDataAccess implementiert die im Interface IDataAccess festgelegten Methoden: insert, update, delete, findAll, insertAll, - sowie alle weiteren Methoden, die die Anwendung benötigen könnte. Hier werden beispielhaft einige der Methoden umgesetzt.

1 insert: Studenten einfügen
Die Methode insert() fügt einen Studenten ein und gibt 1 zurück, wenn der Student eingefügt werden konnte, und ansonsten 0.
2 findAll: Alle Studenten ausgeben
Die Methode findAll() gibt eine Liste aller Studenten zurück, wobei für jeden Studenten auch der Studiengang mit abgefragt wird.

package com.hskl.university.dataaccess;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class StudentDataAccess implements DataAccess<Student>{  
  @Override
  public int insert(Student st) throws SQLException {
    Connection conn = DataSource.getConnection();  
    try {            
      String sql = "INSERT INTO Student(Name, Vorname, Matrikelnr, StudiengangID) VALUES (?, ?, ?, ?)";
      PreparedStatement ps = conn.prepareStatement(sql);      
      ps.setString(1, st.getName());ps.setString(2, st.getVorname());            
      ps.setInt(3, st.getMatrikelnr());  ps.setInt(4, st.getStudiengang().getID());
      int rowcount = ps.executeUpdate();  
      return rowcount;
    } finally {
      conn.close();
    }        
  }
  
  @Override
  public List<Student> findAll() throws SQLException {
      // TODO: Code aus der Slideshow einfügen
  }
}

Weitere Funktionen für den Datenbankzugriff
Eine Auswahl der Methoden / Funktionen, die den Datenbankzugriff in der Klasse StudentDataAccess umsetzen, sind in der Slideshow einsehbar:
insert() - einen Studenten einfügen, insertAll() - eine Liste von Studenten einfügen,
findAll() - alle Zeilen der Tabelle Student auslesen
deleteAll(int lastid) - alle Studenten löschen, deren ID größer ist als lastid.
Einige der Methoden verwenden statische Abfragen (Statement), andere dynamische parametrisierte Abfragen (PreparedStatement).


    Top

5-7 Datenbankzugriffe testen: UniversityClient

Die Klasse UniversityClient ist die Testklasse des Java-Projektes und enthält mit der main-Methode den Einstiegspunkt des Projektes. Der abgebildete Test-Ablauf ist wie folgt: Zunächst wird eine Instanz der Klasse StudentDataAccess angelegt, dadurch haben wir Zugriff auf die im Interface DataAccess festgelegten Methoden: insert, update, delete und findAll. Danach wird erst der Inhalt der Tabelle Student ausgegeben, dann ein neuer Student eingefügt, und dann der Inhalt der Tabelle noch einmal ausgegeben.

  Quellcode UniversityClient.java

package com.hskl.university.dataaccess;
import java.util.ArrayList;
import java.util.List;
public class UniversityClient {
  public static void main(String[] args) {  
    DataAccess<Student> stda = new StudentDataAccess();    
    try {
      List<Student>students = stda.findAll();
      System.out.printf("Vorher: %d Studenten\n", students.size());  
      int deleted = stda.deleteAll(10);
      System.out.println("Gelöscht " + deleted + " Studenten ");
      int inserted = stda.insert(new Student("Test", "Anna", 11222));
      System.out.println("Eingefügt " + inserted + " Studenten ");
      students = stda.findAll();
      System.out.printf("Nachher: %d Studenten\n", students.size());
      System.out.println(students.toString());
    
    } catch (Exception e) {
      e.printStackTrace();
    }  
  }
}

  Ausgabe

Nächste Schritte

Das Tutorial zeigt den Einsatz von JDBC in kleineren prototypischen Anwendungen. Größere Anwendungen, die ihre Daten aus einer Datenbank beziehen, verwenden meist eine Daten-Persistenzschicht, um die Datenobjekte im Arbeitsspeicher vorzuhalten. Die wichtigste Aufgabe der Daten-Persistenzschicht ist das Objekt-Relationale Mapping (ORM), bei dem das objektorientierte Datenmodell der Anwendung auf das relationale Datenmodell der Datenbank abgebildet wird. Die meisten Programmiersprachen bieten ein ORM-Framework an. In Java verwendet man die Java Persistence API (JPA) und Hibernate, in PHP: Doctrine und in Python: SQLAlchemy.

Tools, Quellen und weiterführende Links

Tools:

Quellen und weiterführende Links: