Java Anwendungsentwicklung – Tag 7 von 10
Von Franz-Martin, CTO bei Java Fleet Systems Consulting

Schwierigkeit: 🟡 Fortgeschritten
Voraussetzungen: Tag 6 (JDBC Grundlagen) abgeschlossen


🗺️ Deine Position im Kurs

TagThemaNiveauStatus
1Die Desktop-Ära: Warum GUIs?🟢 Grundlagen✅ Abgeschlossen
2AWT & Swing Grundlagen🟢 Grundlagen✅ Abgeschlossen
3Layouts & Event-Handling🟢 Grundlagen✅ Abgeschlossen
4Komplexe Swing-Komponenten🟡 Fortgeschritten✅ Abgeschlossen
5JavaFX: Die „moderne“ Alternative🔴 KOPFNUSS✅ Abgeschlossen
6JDBC Grundlagen🟢 Grundlagen✅ Abgeschlossen
→ 7JDBC Best Practices🟡 Fortgeschritten👉 DU BIST HIER!
8JPA Einführung🟢 Grundlagen🔜 Kommt als nächstes
9JPA CRUD & Queries🟡 Fortgeschritten🔒 Gesperrt
10Integration & Ausblick🔴 KOPFNUSS🔒 Gesperrt

Legende: 🟢 Einsteiger-freundlich | 🟡 Erfordert Grundlagen | 🔴 Optional/Anspruchsvoll


⚡ Das Wichtigste in 30 Sekunden

Gestern: JDBC funktioniert.
Heute: JDBC in Produktion.

Die drei Säulen produktionsreifer JDBC-Anwendungen:

  1. Connection Pooling → Performance (100x schneller!)
  2. Transactions → Datenintegrität (Alles oder Nichts)
  3. DAO Pattern → Saubere Architektur (Wartbarkeit)

🟢 GRUNDLAGEN: Connection Pooling

Das Problem ohne Pool

JDBC Best

Abbildung 1: Ohne Pool ist jede Connection teuer – Mit Pool sind sie sofort verfügbar


Ohne Pool: Jeder Request baut eine neue Connection auf.

// Bei JEDEM Request:
Connection conn = DriverManager.getConnection(url, user, pass);
// → TCP-Handshake + Authentifizierung + Setup = 50-200ms!

Das summiert sich:

  • 100 Requests/Sekunde × 100ms = 10 Sekunden reine Wartezeit!
  • Plus: Datenbank hat nur begrenzte Connections

Mit Pool: Connections werden wiederverwendet.

// Einmal beim Start:
HikariDataSource dataSource = new HikariDataSource(config);

// Bei JEDEM Request:
Connection conn = dataSource.getConnection();  // ~0-2ms!
// ...
conn.close();  // Gibt zurück in den Pool, schließt NICHT!

HikariCP konfigurieren

HikariCP ist der Standard – Spring Boot verwendet ihn automatisch.

HikariConfig config = new HikariConfig();

// Verbindungsdaten
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("user");
config.setPassword("pass");

// Pool-Größe
config.setMaximumPoolSize(10);    // Max 10 Connections gleichzeitig
config.setMinimumIdle(2);          // Min 2 immer bereit

// Timeouts
config.setConnectionTimeout(30000);  // 30s warten auf Connection
config.setIdleTimeout(600000);       // 10min bis idle geschlossen
config.setMaxLifetime(1800000);      // 30min max Lebenszeit

// Pool erstellen
HikariDataSource dataSource = new HikariDataSource(config);

Faustregel für Pool-Größe:

connections = (core_count * 2) + effective_spindle_count

TeilBedeutung
core_countAnzahl CPU-Kerne
*2Weil Threads bei I/O warten (Hyper-Threading-Effekt)
effective_spindle_countAnzahl Festplatten/SSDs (bei SSD meist 1)

Für die meisten Apps: 10-20 Connections reichen!


🟡 PROFESSIONALS: Transactions

Warum Transactions?

Abbildung 2: Ohne Transaction kann Geld „verschwinden“


Das klassische Beispiel: Banküberweisung.

// OHNE Transaction - GEFÄHRLICH!
stmt.executeUpdate("UPDATE konto SET stand=stand-100 WHERE id='A'");
// 💥 Server crasht hier!
stmt.executeUpdate("UPDATE konto SET stand=stand+100 WHERE id='B'");
// → 100€ verschwunden!

Mit Transaction:

conn.setAutoCommit(false);  // ⚠️ WICHTIG!

try {
    stmt.executeUpdate("UPDATE konto SET stand=stand-100 WHERE id='A'");
    // 💥 Server crasht hier!
    stmt.executeUpdate("UPDATE konto SET stand=stand+100 WHERE id='B'");
    
    conn.commit();  // ✅ Beide Änderungen werden gespeichert
    
} catch (Exception e) {
    conn.rollback();  // 🔙 Alle Änderungen rückgängig!
    throw e;
    
} finally {
    conn.setAutoCommit(true);  // Wichtig für den Pool!
}

ACID – Die 4 Garantien

BuchstabeBedeutungErklärung
AAtomicityAlles oder nichts
CConsistencyDB bleibt in konsistentem Zustand
IIsolationTransaktionen beeinflussen sich nicht
DDurabilityNach Commit ist es dauerhaft

🟡 Das DAO Pattern

Warum DAO?

Abbildung 3: DAO trennt Geschäftslogik von Datenbankzugriff


Ohne DAO: SQL überall verstreut.

// In PersonService.java
String sql = "SELECT * FROM personen WHERE...";
// In BestellService.java
String sql = "SELECT * FROM personen JOIN...";
// In ReportService.java  
String sql = "SELECT name FROM personen WHERE...";
// → Chaos! Änderungen an der Tabelle = überall suchen

Mit DAO: SQL nur an einer Stelle.

// PersonDao Interface
public interface PersonDao {
    Optional<Person> findById(int id);
    List<Person> findAll();
    Person save(Person person);
    boolean update(Person person);
    boolean deleteById(int id);
}

// Service-Code - KEIN SQL!
public class PersonService {
    private final PersonDao personDao;
    
    public Person registrieren(String name, int alter) {
        Person person = new Person(name, alter, null);
        return personDao.save(person);  // SQL ist im DAO gekapselt!
    }
}

Die Implementation

public class PersonDaoJdbc implements PersonDao {
    
    private final DataSource dataSource;
    
    public PersonDaoJdbc(DataSource dataSource) {
        this.dataSource = dataSource;  // Dependency Injection!
    }
    
    @Override
    public Optional<Person> findById(int id) {
        String sql = "SELECT id, name, alter, email FROM personen WHERE id = ?";
        
        try (Connection conn = dataSource.getConnection();
             PreparedStatement ps = conn.prepareStatement(sql)) {
            
            ps.setInt(1, id);
            
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    return Optional.of(mapRow(rs));
                }
            }
        } catch (SQLException e) {
            throw new DaoException("Fehler beim Laden", e);
        }
        
        return Optional.empty();
    }
    
    // Row Mapper - zentralisiert!
    private Person mapRow(ResultSet rs) throws SQLException {
        return new Person(
            rs.getInt("id"),
            rs.getString("name"),
            rs.getInt("alter"),
            rs.getString("email")
        );
    }
}

Vorteile des DAO Patterns

VorteilErklärung
🔄 AustauschbarJDBC → JPA → MongoDB ohne Service-Änderung
🧪 TestbarMock-DAO für Unit Tests
📦 Saubere TrennungSQL nur im DAO
♻️ WiederverwendbarEin DAO für alle Services

💬 Real Talk: Der Performance-Bug

Java Fleet Büro, Montag 9:00 Uhr.


Code Sentinel: „Franz-Martin, die Produktion ist langsam. Sehr langsam.“

Franz-Martin: „Wie langsam?“

Code Sentinel: „Requests brauchen 3-5 Sekunden. Vorher waren es 200ms.“

Franz-Martin: „Was hat sich geändert?“

Code Sentinel: „Nova hat letzte Woche das neue Reporting-Feature deployed.“

Franz-Martin: „Nova, zeig mal den Code.“

Nova: (nervös) „Hier… für jeden Report hole ich die Daten einzeln…“

for (Person person : allePersonen) {
    Connection conn = DriverManager.getConnection(url, user, pass);
    // ... Query ...
    conn.close();
}

Franz-Martin: „Da ist das Problem. 1000 Personen = 1000 neue Connections.“

Nova: „Aber ich schließe sie doch!“

Franz-Martin: „Schließen ist nicht das Problem. Das AUFBAUEN dauert. Jede Connection: TCP-Handshake, SSL, Authentifizierung…“

Nova: „Wie fix ich das?“

Franz-Martin: „Connection Pool. Eine Connection aus dem Pool holen dauert Mikrosekunden, nicht Millisekunden.“

// Einmal beim Start
HikariDataSource ds = new HikariDataSource(config);

// Im Loop
for (Person person : allePersonen) {
    Connection conn = ds.getConnection();  // Aus dem Pool!
    // ... Query ...
    conn.close();  // Zurück in den Pool!
}

Nova: „Das war’s?“

Franz-Martin: „Das war’s. Aber noch besser: Hol ALLE Personen in EINER Query statt 1000 einzelne.“

Nova: „Oh… SELECT * FROM personen WHERE id IN (…)?“

Franz-Martin: „Genau. Das nennt sich ‚N+1 Problem‘. Wirst du noch öfter sehen.“


✅ Checkpoint

📝 Quiz

Frage 1: Was ist der Hauptvorteil von Connection Pooling?

A) Mehr Sicherheit
B) Weniger Code
C) Connections werden wiederverwendet statt neu erstellt
D) Automatische Transaktionen


Frage 2: Was passiert bei conn.close() wenn die Connection aus einem Pool kommt?

A) Die Connection wird geschlossen
B) Die Connection wird zurück in den Pool gegeben
C) Ein Fehler wird geworfen
D) Nichts


Frage 3: Wann sollte man conn.rollback() aufrufen?

A) Nach jedem erfolgreichen Statement
B) Vor commit()
C) Wenn ein Fehler auftritt und die Änderungen rückgängig gemacht werden sollen
D) Nie, das macht JDBC automatisch


Frage 4: Was ist der Hauptzweck des DAO Patterns?

A) Performance verbessern
B) Datenbankzugriff von Geschäftslogik trennen
C) SQL automatisch generieren
D) Connection Pooling implementieren


📝 Quiz-Lösungen

Frage 1:C – Connections werden wiederverwendet
Das Erstellen neuer Connections ist teuer (50-200ms). Pool: <2ms.

Frage 2:B – Zurück in den Pool
Der Pool „täuscht“ das Schließen vor. Die Connection bleibt offen für den nächsten Request.

Frage 3:C – Bei Fehlern
Rollback macht alle Änderungen seit dem letzten Commit rückgängig.

Frage 4:B – Trennung von Datenbankzugriff und Geschäftslogik
Der Service-Code sieht kein SQL. Das DAO kapselt alles.


❓ FAQ

Wie groß sollte mein Connection Pool sein?
Faustregel: (CPU-Kerne × 2) + Anzahl Festplatten. Für die meisten Apps: 10-20 reichen. Mehr Connections ≠ mehr Performance!

Muss ich Transactions immer manuell steuern?
In reinem JDBC: Ja. Mit Spring: Nein, @Transactional macht das automatisch. JPA hat auch eigene Transaction-Verwaltung.

Warum HikariCP und nicht C3P0 oder DBCP?
HikariCP ist schneller, hat weniger Bugs, und ist der Spring Boot Default. Benchmark: HikariCP schlägt alle anderen um Faktor 2-10.

Wann DAO, wann Repository Pattern?
DAO = feingranulare CRUD-Operationen. Repository = Domain-orientiert (Aggregate Roots). In der Praxis oft synonym verwendet.

Muss ich für jede Tabelle ein DAO schreiben?
Mit JDBC: Ja, das ist viel Boilerplate. Mit JPA: JpaRepository<Entity, ID> und fertig. Das zeigen wir in Tag 8-9!


📦 Downloads

ProjektInhaltDownload
java-anwendungsentwicklung-tag7.zipPool, Transactions, DAO⬇️ Download

Quick Start:

mvn exec:java                # Connection Pool Demo
mvn exec:java -Ptransaction  # Transaction Demo
mvn exec:java -Pdao          # DAO Pattern Demo

🔗 Weiterführende Links

RessourceBeschreibung
HikariCPDer schnellste Connection Pool
DAO Pattern (Oracle)Offizielle Dokumentation
ACID (Wikipedia)Transaction-Garantien

🎉 Tag 7 geschafft!

Was du heute gelernt hast:

✅ Connection Pooling mit HikariCP (100x schneller!)
✅ Transactions für Datenintegrität (commit/rollback)
✅ ACID-Prinzipien verstehen
✅ DAO Pattern für saubere Architektur
✅ Dependency Injection für DAOs

Morgen – Tag 8: JPA Einführung

Schluss mit dem JDBC-Boilerplate! JPA generiert SQL automatisch und macht PersonDaoJdbc zu einer einzigen Zeile.


Fragen? franz-martin@java-developer.online

© 2026 Java Fleet Systems Consulting | java-developer.online

Autor

  • Franz-Martin

    65 Jahre alt, CTO und Gründer von Java Fleet Systems Consulting. Franz-Martin ist erfahrener Java-Entwickler, Tutor und Dozent, der das Unternehmen gegründet hat, um sein Wissen weiterzugeben und echte Java-Probleme zu lösen. Er moderiert Team-Diskussionen, mentoriert alle Crew-Mitglieder und sorgt dafür, dass technische Exzellenz mit Business-Realität kombiniert wird.