Zum Inhalt
wannacode

Aus dem Code mit der Datenbank verbinden

Kurz gesagt: Ein Programm spricht mit einer Datenbank über einen Treiber: Es öffnet eine Verbindung zur Adresse der Datenbank, schickt die Abfrage, liest die Zeilen eine nach der anderen und schließt die Verbindung.

Problem

Die Seite „Unter 5 €“

Die Website des Shops hat eine Seite mit den günstigen Produkten. Die Abfrage dafür kennst du schon:

SQLDatenbank: Shop

Aber niemand auf der Website tippt Abfragen in ein Feld. Wenn jemand die Seite öffnet, führt der Server des Shops ein Programm aus. Das Programm schickt diese Abfrage an die Datenbank, bekommt die 17 Zeilen als Werte seiner eigenen Sprache zurück (ein String, eine Zahl) und setzt sie ins HTML. Wie genau spricht ein Programm mit einer Datenbank?

Die Idee

Die fünf Schritte

In jeder Sprache ist es dasselbe:

  1. Treiber. Eine Bibliothek, die die Sprache einer bestimmten Datenbank spricht (SQLite, PostgreSQL, MySQL).
  2. Verbindung. Das Programm sagt, wo die Datenbank ist (ihre Adresse), und verbindet sich.
  3. Abfrage. Der SQL-Text geht an die Datenbank.
  4. Zeilen. Das Ergebnis kommt Zeile für Zeile zurück, und das Programm liest seine Spalten als Werte.
  5. Schließen. Die Verbindung wird freigegeben, auch wenn mittendrin ein Fehler passiert ist.

Hier ist dieselbe Abfrage mit einer kleinen Kopie des Shops im Speicher (:memory:, sechs Produkte, die setup am Anfang anlegt):

try (Connection conn = DriverManager.getConnection("jdbc:sqlite::memory:")) {
setup(conn);
String sql = "SELECT name, price FROM products WHERE price < 5 ORDER BY price";
try (Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql)) {
while (rs.next()) {
System.out.printf("%s: %.2f €%n", rs.getString("name"), rs.getDouble("price"));
}
}
}
Ausgabe
Butterbrötchen: 0.80 €
Weißbrot: 1.20 €
Frischmilch: 1.60 €
Butter: 3.20 €

Um den fünften Schritt kümmert sich die Sprache: try (…) in Java, using in C#, close() in Python und JavaScript. In Python beendet with die Transaktion, nicht die Verbindung, deshalb steht close() hier ausdrücklich da.

Genauer, für Neugierige

setup führt den SQL-Text aus, der die Tabelle mit den sechs Produkten anlegt:

static void setup(Connection conn) throws SQLException {
try (Statement st = conn.createStatement()) {
st.executeUpdate("""
CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price REAL NOT NULL, discount INTEGER);
INSERT INTO products VALUES
(1, 'Weißbrot', 1.20, NULL), (3, 'Butterbrötchen', 0.80, 10), (5, 'Frischmilch', 1.60, NULL),
(7, 'Weißkäse', 7.90, 15), (9, 'Butter', 3.20, NULL), (17, 'Kaffeebohnen', 14.90, 0);
""");
}
}

Die Treiber in den Beispielen: sqlite-jdbc in Java (das Paket org.xerial:sqlite-jdbc), Microsoft.Data.Sqlite in C# (NuGet), und in Python und JavaScript ist SQLite eingebaut (sqlite3, und node:sqlite seit Node 22). Jedes Beispiel wurde ausgeführt, und seine Ausgabe ist genau diese.

Sieh es dir an

Die Adresse der Datenbank und das Passwort

SQLite ist eine Datei, und die Adresse ist der Pfad dazu. Wenn du in der Lektion „SQL und echte Datenbanken“ eine shop.db angelegt hast (sqlite3 shop.db < shop-de.sql), ersetze :memory: durch den Pfad und lass setup weg: In Java ist es jdbc:sqlite:shop.db, in C# Data Source=shop.db, und in Python und JavaScript einfach "shop.db". Dasselbe Programm zeigt dann alle 17 Produkte.

Eine Server-Datenbank (PostgreSQL, MySQL, SQL Server) lebt in einem anderen Programm, oft auf einem anderen Computer. Ihre Adresse hat einen Host, einen Port, einen Datenbanknamen, einen Benutzer und ein Passwort:

String url = "jdbc:postgresql://localhost:5432/shop";
String password = System.getenv("SHOP_DB_PASSWORD");
try (Connection conn = DriverManager.getConnection(url, "shop_app", password)) {
// dieselben Abfragen
}

Das Passwort steht nicht im Code. Der Code kommt in git, wo jeder mit Zugriff ihn sieht, und das Passwort der echten Seite ist ein anderes als das auf deinem Computer. Es kommt also von außen: aus einer Umgebungsvariablen wie hier oder aus dem Secrets-Speicher des Hosting-Anbieters.

Diese vier Beispiele laufen nicht in den Prüfungen der Seite, weil sie einen PostgreSQL-Server brauchen. Die Treiber sind org.postgresql, Npgsql, psycopg und pg.

Probier es aus

Die Abfrage für eine andere Seite

Die Seite „Ausverkauft“ im Admin-Bereich zeigt die Namen der Produkte mit Bestand 0, in alphabetischer Reihenfolge. Schreib die Abfrage, die das Programm schicken wird.

SQLDatenbank: Shop

Die Idee

Von der Spalte zum Wert

Jede Spalte kommt als Wert eines Typs der Sprache an. INTEGER wird zu int (oder long), REAL wird zu double, TEXT wird zu String / string / str. Interessant ist NULL. In Python ist es None, in JavaScript null, und in Java und C# hängt es davon ab, wie du es liest:

try (Connection conn = DriverManager.getConnection("jdbc:sqlite::memory:")) {
setup(conn);
try (Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("SELECT name, discount FROM products WHERE id IN (1, 3, 17)")) {
while (rs.next()) {
int discount = rs.getInt("discount");
String text = rs.wasNull() ? "kein Rabatt" : discount + "%";
System.out.println(rs.getString("name") + ": getInt liefert " + discount + ", also " + text);
}
}
}
Ausgabe
Weißbrot: getInt liefert 0, also kein Rabatt
Butterbrötchen: getInt liefert 10, also 10%
Kaffeebohnen: getInt liefert 0, also 0%

Weißbrot hat keinen Rabatt (NULL), während der Kaffee einen Rabatt von 0 % hat. Das ist nicht dasselbe. In Java liefert getInt in beiden Fällen 0, und nur wasNull() unterscheidet sie. In C# bricht GetInt32 bei NULL mit einem Fehler ab, deshalb fragst du zuerst mit IsDBNull.

Übung

Die neuen Produkte

Die Startseite zeigt die neuen Produkte: Name und Preis der Produkte, die ab dem 1. Juni 2025 hinzugekommen sind (added_at), die neuesten zuerst, und bei gleichem Datum nach Name.

SQLDatenbank: Shop

Test

Teste dich

1. Das Programm hat eine Zeile mit einem Preis gelesen und angezeigt. Welchen dieser Schritte vergisst man am häufigsten, und er legt später die Seite lahm?
2. Wo sollte das Passwort für die Shop-Datenbank stehen?
3. In Java ist die Spalte discount NULL, und das Programm ruft rs.getInt("discount") auf. Was bekommt es?
4. Die Abfrage ist in allen vier Sprachen gleich. Was ändert sich, wenn der Shop von SQLite zu PostgreSQL wechselt?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·