8. Datenbank-Schnittstellen
Datenbank-Schnittstellen
Abschnitt betitelt „Datenbank-Schnittstellen“Moderne Anwendungen sprechen mit einer Datenbank selten über rohes SQL, das man im Terminal eintippt. Stattdessen nutzen sie standardisierte Datenbank-Schnittstellen — Bibliotheken und Treiber, mit denen Programme in Python, JavaScript, Java, PHP und vielen anderen Sprachen strukturiert, sicher und portabel mit einem RDBMS kommunizieren.
Dieses Kapitel behandelt die wichtigsten Schnittstellenkonzepte und zeigt konkrete Beispiele, wie man aus verbreiteten Programmiersprachen eine Verbindung zu PostgreSQL und MariaDB / MySQL aufbaut und abfragt.
1. Überblick über die Schnittstellenstandards
Abschnitt betitelt „1. Überblick über die Schnittstellenstandards“1.1 Warum Standardisierung?
Abschnitt betitelt „1.1 Warum Standardisierung?“Ohne Standards bräuchte jeder Datenbankhersteller eine eigene proprietäre API. Eine Python-Anwendung, die mit PostgreSQL spricht, sähe völlig anders aus als eine, die mit MariaDB / MySQL oder Oracle spricht. Standards lösen das:
- ODBC (Open Database Connectivity) — ein API-Standard auf C-Ebene, breit über Sprachen und Plattformen unterstützt. Bietet eine gemeinsame Schnittstelle, sodass eine Anwendung mit minimalen Codeänderungen zwischen verschiedenen Datenbank-Backends wechseln kann.
- JDBC (Java Database Connectivity) — das Java-Pendant zu ODBC. Jedes größere RDBMS liefert einen JDBC-Treiber.
- Native Treiber — sprachspezifische Bibliotheken, die direkt mit einem bestimmten DBMS kommunizieren (etwa
psycopg2für Python + PostgreSQL,pgfür Node.js + PostgreSQL). Oft schneller und funktionsreicher als generische ODBC-/JDBC-Wrapper. - ORMs (Object-Relational Mapper) — höhere Abstraktionen, die Datenbankzeilen auf Objekte der Programmiersprache abbilden (etwa SQLAlchemy für Python, Prisma für Node.js). Nützlich für schnelle Entwicklung, können aber wichtiges SQL-Verhalten verstecken.
1.2 Wie eine Datenbankverbindung funktioniert
Abschnitt betitelt „1.2 Wie eine Datenbankverbindung funktioniert“Unabhängig von Sprache oder Treiber ist der grundsätzliche Ablauf immer gleich:
- Treiber laden — die Anwendung lädt die Treiberbibliothek.
- Verbindung aufbauen — der Treiber verbindet sich über einen Connection String (Host, Port, Datenbankname, Benutzername, Passwort) mit dem DBMS.
- Cursor / Session öffnen — ein logischer Handle, um Abfragen zu senden und Ergebnisse zu empfangen.
- Abfragen ausführen — SQL-Anweisungen werden an die Datenbank gesendet.
- Ergebnisse verarbeiten — die Ergebniszeilen werden abgeholt und in der Anwendung genutzt.
- Verbindung schließen — die Verbindung wird an einen Pool zurückgegeben oder geschlossen.
Anwendung │ ▼ Treiber / Connector (z. B. psycopg2, pg, JDBC-Treiber) │ ▼ Netzwerk (TCP/IP, Unix-Socket) │ ▼ PostgreSQL-Server (Port 5432) │ ▼ Database / Schema / Tables1.3 Connection Strings
Abschnitt betitelt „1.3 Connection Strings“Die meisten Treiber akzeptieren einen Connection String (auch DSN, Data Source Name), der alle Verbindungsparameter kodiert:
postgresql://username:password@host:port/databaseBeispiel:
postgresql://app_user:secret@localhost:5432/school_db2. Verbindung aus Node.js
Abschnitt betitelt „2. Verbindung aus Node.js“Node.js-Anwendungen nutzen üblicherweise pg für PostgreSQL und mysql2 für MariaDB / MySQL.
2.1 Installation
Abschnitt betitelt „2.1 Installation“npm install pgnpm install mysql22.2 Einfache Abfrage
Abschnitt betitelt „2.2 Einfache Abfrage“import pg from 'pg';const { Pool } = pg;
const pool = new Pool({ host: process.env.DB_HOST, port: process.env.DB_PORT || 5432, database: process.env.DB_NAME, user: process.env.DB_USER, password: process.env.DB_PASSWORD,});
async function getActiveStudents() { const result = await pool.query( 'SELECT student_id, name FROM students WHERE is_active = $1 ORDER BY name', [true] // parameterized - $1 is the placeholder ); return result.rows;}
getActiveStudents().then(console.log);import mysql from 'mysql2/promise';
const pool = mysql.createPool({ host: process.env.DB_HOST, port: process.env.DB_PORT || 3306, database: process.env.DB_NAME, user: process.env.DB_USER, password: process.env.DB_PASSWORD,});
async function getActiveStudents() { const [rows] = await pool.execute( 'SELECT student_id, name FROM students WHERE is_active = ? ORDER BY name', [true] // parameterized - ? is the placeholder ); return rows;}
getActiveStudents().then(console.log);Zu beachten ist, dass die Platzhalter in Node.js je nach Treiber unterschiedlich sind: PostgreSQL (pg) nutzt $1, $2, …, während MariaDB / MySQL (mysql2) ? verwendet.
2.3 INSERT mit Rückgabe
Abschnitt betitelt „2.3 INSERT mit Rückgabe“async function createStudent(name) { const result = await pool.query( 'INSERT INTO students (name, is_active) VALUES ($1, $2) RETURNING student_id', [name, true] ); return result.rows[0].student_id;}async function createStudent(name) { const [result] = await pool.execute( 'INSERT INTO students (name, is_active) VALUES (?, ?)', [name, true] ); return result.insertId;}2.4 Transaktionen in Node.js
Abschnitt betitelt „2.4 Transaktionen in Node.js“async function transferCredits(fromId, toId, amount) { const client = await pool.connect(); try { await client.query('BEGIN'); await client.query( 'UPDATE accounts SET balance = balance - $1 WHERE account_id = $2', [amount, fromId] ); await client.query( 'UPDATE accounts SET balance = balance + $1 WHERE account_id = $2', [amount, toId] ); await client.query('COMMIT'); } catch (err) { await client.query('ROLLBACK'); throw err; } finally { client.release(); // return the client to the pool }}async function transferCredits(fromId, toId, amount) { const conn = await pool.getConnection(); try { await conn.beginTransaction(); await conn.execute( 'UPDATE accounts SET balance = balance - ? WHERE account_id = ?', [amount, fromId] ); await conn.execute( 'UPDATE accounts SET balance = balance + ? WHERE account_id = ?', [amount, toId] ); await conn.commit(); } catch (err) { await conn.rollback(); throw err; } finally { conn.release(); // return the connection to the pool }}2.5 Fetch Size und Pagination in Node.js
Abschnitt betitelt „2.5 Fetch Size und Pagination in Node.js“Bei großen Ergebnismengen sollte man nicht alles auf einmal in den Speicher laden. Für UI-/API-Antworten dient Pagination, für Exporte Streaming bzw. Cursor.
OFFSET/LIMIT-Pagination (einfach)
Abschnitt betitelt „OFFSET/LIMIT-Pagination (einfach)“const pageSize = 50;const page = 0;const offset = page * pageSize;
const result = await pool.query( `SELECT student_id, name FROM students ORDER BY student_id LIMIT $1 OFFSET $2`, [pageSize, offset]);
return result.rows;const pageSize = 50;const page = 0;const offset = page * pageSize;
const [rows] = await pool.execute( `SELECT student_id, name FROM students ORDER BY student_id LIMIT ? OFFSET ?`, [pageSize, offset]);
return rows;Keyset-Pagination (empfohlen für große Tabellen)
Abschnitt betitelt „Keyset-Pagination (empfohlen für große Tabellen)“Keyset-Pagination ist meist schneller und stabiler als große Offsets, weil sie den zuletzt gesehenen Schlüssel nutzt.
const lastSeenId = 1200;const pageSize = 50;
const result = await pool.query( `SELECT student_id, name FROM students WHERE student_id > $1 ORDER BY student_id LIMIT $2`, [lastSeenId, pageSize]);
return result.rows;const lastSeenId = 1200;const pageSize = 50;
const [rows] = await pool.execute( `SELECT student_id, name FROM students WHERE student_id > ? ORDER BY student_id LIMIT ?`, [lastSeenId, pageSize]);
return rows;Fetch Size / Streaming
Abschnitt betitelt „Fetch Size / Streaming“- Mit
pgundmysql2liefern normale Query-Aufrufe die gesamte Ergebnismenge in den Speicher. - Für sehr große Exporte streaming- oder cursorbasierten Zugriff verwenden und Zeilen in Blöcken verarbeiten.
- Immer einen Index auf den in
WHEREundORDER BYgenutzten Spalten für die Pagination halten.
4. Verbindung aus anderen Sprachen
Abschnitt betitelt „4. Verbindung aus anderen Sprachen“4.1 PHP (PDO)
Abschnitt betitelt „4.1 PHP (PDO)“PHPs PDO (PHP Data Objects) ist eine Datenbank-Abstraktionsschicht ähnlich ODBC. Es arbeitet mit mehreren RDBMS-Backends.
<?php$dsn = 'pgsql:host=localhost;port=5432;dbname=school_db';$user = getenv('DB_USER');$pass = getenv('DB_PASSWORD');
$pdo = new PDO($dsn, $user, $pass, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,]);
$stmt = $pdo->prepare('SELECT name FROM students WHERE student_id = :id');$stmt->execute([':id' => 42]);$student = $stmt->fetch(PDO::FETCH_ASSOC);echo $student['name'];?>4.2 Python
Abschnitt betitelt „4.2 Python“Für den Zugriff aus Python eignet sich dieses minimale Setup:
pip install psycopg2-binary python-dotenvfrom dotenv import load_dotenvimport osimport psycopg2
load_dotenv()
conn = psycopg2.connect( host=os.environ["DB_HOST"], port=int(os.environ.get("DB_PORT", 5432)), dbname=os.environ["DB_NAME"], user=os.environ["DB_USER"], password=os.environ["DB_PASSWORD"])
with conn: with conn.cursor() as cur: cur.execute("SELECT student_id, name FROM students WHERE name = %s", ("Anna",)) print(cur.fetchall())pip install mysql-connector-python python-dotenvfrom dotenv import load_dotenvimport osimport mysql.connector
load_dotenv()
conn = mysql.connector.connect( host=os.environ["DB_HOST"], port=int(os.environ.get("DB_PORT", 3306)), database=os.environ["DB_NAME"], user=os.environ["DB_USER"], password=os.environ["DB_PASSWORD"])
cur = conn.cursor()cur.execute("SELECT student_id, name FROM students WHERE name = %s", ("Anna",))print(cur.fetchall())cur.close()conn.close()4.3 Java (JDBC)
Abschnitt betitelt „4.3 Java (JDBC)“import java.sql.*;
String url = "jdbc:postgresql://localhost:5432/school_db";String user = System.getenv("DB_USER");String password = System.getenv("DB_PASSWORD");
try (Connection conn = DriverManager.getConnection(url, user, password); PreparedStatement stmt = conn.prepareStatement( "SELECT name FROM students WHERE student_id = ?")) { stmt.setInt(1, 42); try (ResultSet rs = stmt.executeQuery()) { if (rs.next()) { System.out.println(rs.getString("name")); } }}4.4 ODBC (Excel, Power BI, generische Werkzeuge)
Abschnitt betitelt „4.4 ODBC (Excel, Power BI, generische Werkzeuge)“ODBC erlaubt Desktop-Werkzeugen wie Excel oder Power BI, sich direkt mit PostgreSQL und MariaDB / MySQL zu verbinden:
- Den PostgreSQL-ODBC-Treiber (
psqlODBC) unter Windows installieren. - ODBC-Datenquellen in Windows öffnen und einen neuen DSN (Data Source Name) anlegen.
- In Excel unter Daten → Daten abrufen → Aus Datenbank → Aus ODBC den DSN auswählen.
- In Power BI Daten abrufen → ODBC wählen und den DSN auswählen.
- Einen MariaDB/MySQL-ODBC-Treiber unter Windows installieren (etwa MariaDB Connector/ODBC oder MySQL Connector/ODBC).
- ODBC-Datenquellen in Windows öffnen und einen neuen DSN (Data Source Name) anlegen.
- In Excel unter Daten → Daten abrufen → Aus Datenbank → Aus ODBC den DSN auswählen.
- In Power BI Daten abrufen → ODBC wählen und den DSN auswählen.
DSN-lose Verbindungen
Abschnitt betitelt „DSN-lose Verbindungen“Statt vorab einen DSN im ODBC-Administrator zu konfigurieren, lassen sich alle Verbindungsparameter direkt in den Connection String der Anwendung einbetten:
DRIVER={PostgreSQL Unicode};SERVER=localhost;PORT=5432;DATABASE=school_db;UID=app_user;PWD=secret;DRIVER={MariaDB ODBC 3.1 Driver};SERVER=localhost;PORT=3306;DATABASE=school_db;UID=app_user;PWD=secret;Das erspart die manuelle DSN-Einrichtung auf jedem Rechner und ist nützlich für automatisierte Deployments oder wenn die Anwendung an viele Nutzer verteilt wird.
Unicode- vs. ANSI-Treiber
Abschnitt betitelt „Unicode- vs. ANSI-Treiber“Für PostgreSQL kommt psqlODBC in zwei Varianten:
- PostgreSQL Unicode — behandelt UTF-8 und internationale Zeichen korrekt. Immer diese bevorzugen.
- PostgreSQL ANSI — Altvariante für ältere Anwendungen, die ausdrücklich ANSI-Kodierung verlangen.
Für MariaDB / MySQL einen modernen, Unicode-fähigen ODBC-Treiber wählen (etwa MariaDB Connector/ODBC Unicode oder MySQL Connector/ODBC Unicode), sofern nicht eine ältere reine ANSI-Umgebung etwas anderes verlangt.
User-DSN vs. System-DSN
Abschnitt betitelt „User-DSN vs. System-DSN“Beim Anlegen eines DSN im ODBC-Administrator wählt man zwischen zwei Geltungsbereichen:
| Typ | Sichtbar für | Typischer Einsatz |
|---|---|---|
| User-DSN | nur den aktuellen Windows-Benutzer | persönliche Werkzeuge, Entwicklung |
| System-DSN | alle Nutzer und Windows-Dienste | gemeinsam genutzte Anwendungen, Dienste |
Windows-Dienste (etwa ein Hintergrund-Sync-Job) laufen unter einem Dienstkonto und sehen User-DSNs meist nicht. Für serverseitige oder gemeinsam genutzte Anwendungen immer einen System-DSN verwenden.
SSL/TLS
Abschnitt betitelt „SSL/TLS“psqlODBC unterstützt den Parameter sslmode, passend zu den Standard-SSL-Modi von PostgreSQL:
sslmode-Wert | Verhalten |
|---|---|
disable | kein SSL (Klartext) |
require | SSL erforderlich, Zertifikat nicht geprüft |
verify-ca | SSL + Serverzertifikat gegen eine CA prüfen |
verify-full | SSL + Zertifikat und Hostname prüfen |
Füge ihn den erweiterten DSN-Optionen oder einem DSN-losen Connection String hinzu:
...;SSLmode=require;In jeder Produktions- oder Netzwerkumgebung mindestens require verwenden.
Zugangsdaten in DSNs
Abschnitt betitelt „Zugangsdaten in DSNs“System-DSNs speichern den Benutzernamen (und optional das Passwort) in der Windows-Registry. Das Passwort ist für lokale Administratoren sichtbar. Für Produktivsysteme:
- Das Passwort nicht im DSN speichern.
- Die Anwendung oder den Nutzer das Passwort zur Verbindungszeit angeben lassen (Abfrage).
- Alternativ Windows-integrierte Authentifizierung (SSPI/Kerberos) nutzen, wenn der PostgreSQL-Server dafür konfiguriert ist.
Timeout-Einstellungen
Abschnitt betitelt „Timeout-Einstellungen“Standardmäßig können ODBC-Verbindungen und -Abfragen bei Netzwerkproblemen unbegrenzt hängen. Diese Parameter gehören in den DSN oder Connection String:
LoginTimeout— Sekunden, die beim Verbindungsaufbau gewartet wird (z. B.LoginTimeout=10).QueryTimeout— Sekunden, bevor eine laufende Abfrage abgebrochen wird (in den meisten Treibern pro Statement konfigurierbar).
Ohne Timeouts kann eine unterbrochene Netzwerkverbindung eine Excel-Tabelle oder Anwendung unbegrenzt einfrieren.
Plattformübergreifende Hinweise (Windows, macOS, Linux)
Abschnitt betitelt „Plattformübergreifende Hinweise (Windows, macOS, Linux)“Das ODBC-Konzept existiert auf allen großen Betriebssystemen, aber die Einrichtung unterscheidet sich:
| Thema | Windows | macOS | Linux |
|---|---|---|---|
| ODBC-Manager | eingebauter ODBC-Administrator | meist unixODBC oder iODBC | meist unixODBC |
| DSN-Speicherung | Registry (User-/System-DSN) | ~/.odbc.ini und /etc/odbc.ini (üblich) | ~/.odbc.ini und /etc/odbc.ini |
| Treiberinstallation | MSI/EXE-Installer | Homebrew/PKG (treiberabhängig) | Paketmanager + Konfigurationsdateien |
| Plattform-Falle | 32-Bit vs. 64-Bit passt nicht | Intel vs. Apple Silicon (x86_64 vs. arm64) | fehlende Shared Libraries / Treiberpfade |
Weitere praktische Punkte:
- Bitness/Architektur muss passen: Unter Windows müssen App und Treiber beide 32-Bit oder beide 64-Bit sein. Unter macOS die native Architektur prüfen (
arm64auf Apple Silicon), um Rosetta-Probleme zu vermeiden. - Unterschiede bei Treibermanagern:
unixODBCundiODBCwerden nicht gleich konfiguriert. Bei Anleitungen immer die für den tatsächlich installierten Manager passenden Schritte verwenden. - Rechte zählen auf Unix-artigen Systemen: Ein DSN in
/etc/odbc.inibraucht zum Bearbeiten eventuell erhöhte Rechte; benutzerspezifische DSNs in~/.odbc.inisind für die Entwicklung oft besser. - SSL-Zertifikate unterscheiden sich je OS: Truststore und Zertifikatspfade werden unterschiedlich gehandhabt. Funktioniert TLS auf einem OS, aber nicht auf einem anderen, zuerst CA-Pfad/Zertifikatskonfiguration prüfen.
- Verfügbarkeit von BI-Desktop-Werkzeugen unterscheidet sich: Power BI Desktop ist primär Windows-basiert, unter macOS/Linux nutzt man oft Alternativen oder Gateways.
5. Vergleich der Schnittstellenansätze
Abschnitt betitelt „5. Vergleich der Schnittstellenansätze“| Ansatz | Typischer Einsatz | Portabilität | Performance | Komplexität |
|---|---|---|---|---|
| Nativer Treiber (psycopg2, pg) | Python-/Node.js-Web-Apps | niedrig (DB-spezifisch) | hoch | niedrig |
| ODBC | Desktop-Werkzeuge, Multi-DB-Apps | hoch | mittel | mittel |
| JDBC | Java-Enterprise-Apps | hoch (über Treiber) | hoch | mittel |
| ORM (SQLAlchemy, Prisma) | schnelle Entwicklung, CRUD-Apps | hoch | mittel | niedrig–mittel |
| PDO (PHP) | PHP-Web-Apps | mittel | mittel | niedrig |
6. Sicherheits-Checkliste für Datenbank-Schnittstellen
Abschnitt betitelt „6. Sicherheits-Checkliste für Datenbank-Schnittstellen“Sicherer Datenbankzugriff ist ein kritischer Teil der Anwendungssicherheit. Vor dem Produktivgang prüfen:
Zusammenfassung
Abschnitt betitelt „Zusammenfassung“Datenbank-Schnittstellen sind die Brücke zwischen Anwendungscode und Datenbank-Engine. Die wichtigsten Punkte:
- Native Treiber (psycopg2, pg) sind die häufigste Wahl für Python- und Node.js-Anwendungen. ODBC/JDBC bringen Portabilität über RDBMS-Hersteller hinweg. ORMs bieten eine höhere Abstraktion.
- Immer parametrisierte Abfragen verwenden, um SQL-Injection zu verhindern.
- Connection Pools in jeder Mehrbenutzer- oder Web-Anwendung einsetzen.
- Zugangsdaten in Umgebungsvariablen speichern, nie im Quellcode.
- Das Prinzip Least Privilege anwenden: Der Anwendungsbenutzer sollte nur die minimal nötigen Datenbankrechte haben.
- Bei der Fehlersuche sowohl den Anwendungsfehler als auch das PostgreSQL-Server-Log prüfen, um das ganze Bild zu bekommen.