Java Anwendungsentwicklung – Tag 9 von 10
Von Franz-Martin, CTO bei Java Fleet Systems Consulting
Schwierigkeit: 🟡 Fortgeschritten
Voraussetzungen: Tag 8 (JPA Einführung) abgeschlossen
🗺️ Deine Position im Kurs
| Tag | Thema | Niveau | Status |
|---|---|---|---|
| 1 | Die Desktop-Ära: Warum GUIs? | 🟢 Grundlagen | ✅ Abgeschlossen |
| 2 | AWT & Swing Grundlagen | 🟢 Grundlagen | ✅ Abgeschlossen |
| 3 | Layouts & Event-Handling | 🟢 Grundlagen | ✅ Abgeschlossen |
| 4 | Komplexe Swing-Komponenten | 🟡 Fortgeschritten | ✅ Abgeschlossen |
| 5 | JavaFX: Die „moderne“ Alternative | 🔴 KOPFNUSS | ✅ Abgeschlossen |
| 6 | JDBC Grundlagen | 🟢 Grundlagen | ✅ Abgeschlossen |
| 7 | JDBC Best Practices | 🟡 Fortgeschritten | ✅ Abgeschlossen |
| 8 | JPA Einführung | 🟢 Grundlagen | ✅ Abgeschlossen |
| → 9 | JPA CRUD & Queries | 🟡 Fortgeschritten | 👉 DU BIST HIER! |
| 10 | Integration & Ausblick | 🔴 KOPFNUSS | 🔜 Kommt als nächstes |
Legende: 🟢 Einsteiger-freundlich | 🟡 Erfordert Grundlagen | 🔴 Optional/Anspruchsvoll
⚡ Das Wichtigste in 30 Sekunden
Heute lernst du:
- ✅ JPQL – Objekt-orientierte Abfragen
- ✅ Entity Relationships (@OneToMany, @ManyToOne, @OneToOne)
- ✅ JOIN FETCH – Das N+1-Problem vermeiden
- ✅ Das Repository Pattern
Der Unterschied zu SQL:
// SQL: Tabellen und Spalten SELECT * FROM personen WHERE alter > 25 // JPQL: Entities und Felder SELECT p FROM Person p WHERE p.alter > 25
🟢 GRUNDLAGEN: JPQL
Was ist JPQL?

Abbildung 1: JPQL arbeitet mit Objekten, nicht mit Tabellen
Java Persistence Query Language ist wie SQL, aber:
- Arbeitet mit Entity-Namen (Person), nicht Tabellennamen (personen)
- Arbeitet mit Feld-Namen (alter), nicht Spaltennamen
- Gibt Java-Objekte zurück, nicht Zeilen
Grundlegende JPQL-Abfragen
// Alle laden
List<Person> alle = em.createQuery(
"SELECT p FROM Person p",
Person.class
).getResultList();
// Mit WHERE
List<Person> ergebnis = em.createQuery(
"SELECT p FROM Person p WHERE p.alter > :minAlter",
Person.class
).setParameter("minAlter", 25)
.getResultList();
// LIKE-Suche
List<Person> muellers = em.createQuery(
"SELECT p FROM Person p WHERE p.name LIKE :name",
Person.class
).setParameter("name", "%Müller%")
.getResultList();
// Aggregat-Funktionen
Long anzahl = em.createQuery(
"SELECT COUNT(p) FROM Person p",
Long.class
).getSingleResult();
// Pagination
List<Person> seite1 = em.createQuery("SELECT p FROM Person p", Person.class)
.setFirstResult(0) // Offset
.setMaxResults(10) // Limit
.getResultList();
🟡 PROFESSIONALS: Entity Relationships
Die 3 Relationship-Typen

Abbildung 2: @OneToOne, @OneToMany/@ManyToOne, @ManyToMany
@OneToOne: Person → Adresse
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "adresse_id")
private Adresse adresse;
}
// Verwendung:
Person max = new Person("Max", 28, "max@ex.com");
max.setAdresse(new Adresse("Hauptstr. 1", "12345", "Berlin"));
em.persist(max); // Adresse wird MIT gespeichert!
@OneToMany / @ManyToOne: Person ↔ Bestellungen
@Entity
public class Person {
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
private List<Bestellung> bestellungen = new ArrayList<>();
// Helper-Methode
public void addBestellung(Bestellung b) {
bestellungen.add(b);
b.setPerson(this); // Beide Seiten verknüpfen!
}
}
@Entity
public class Bestellung {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "person_id")
private Person person;
}
Das N+1-Problem und JOIN FETCH
// ❌ SCHLECHT: N+1 Queries!
List<Person> personen = em.createQuery("SELECT p FROM Person p").getResultList();
for (Person p : personen) {
p.getBestellungen().size(); // → Extra Query pro Person!
}
// ✅ GUT: 1 Query mit JOIN FETCH
List<Person> personen = em.createQuery(
"SELECT DISTINCT p FROM Person p LEFT JOIN FETCH p.bestellungen",
Person.class
).getResultList();
🟡 Die 3 Query-Methoden

Abbildung 3: JPQL, Criteria API, Native SQL
| Methode | Wann verwenden? |
|---|---|
| JPQL | Die meisten Fälle – lesbar, datenbankunabhängig |
| Criteria API | Dynamische Filter (z.B. Suchformulare) |
| Native SQL | Komplexe Reports, DB-spezifische Features |
// JPQL (empfohlen für 90% der Fälle)
em.createQuery("SELECT p FROM Person p WHERE p.alter > :min", Person.class)
.setParameter("min", 25);
// Native SQL (wenn nötig)
em.createNativeQuery("SELECT * FROM personen WHERE alter > ?1", Person.class)
.setParameter(1, 25);
🟡 Das Repository Pattern
// Manuell implementiert (wie heute)
public class PersonRepository {
public List<Person> findAll() { ... }
public Optional<Person> findById(Long id) { ... }
public Person save(Person person) { ... }
public void deleteById(Long id) { ... }
}
// Mit Spring Data JPA (morgen):
public interface PersonRepository extends JpaRepository<Person, Long> {
List<Person> findByNameContaining(String name);
}
// Das ist ALLES - Spring generiert die Implementation!
💬 Real Talk: Das N+1-Problem
Java Fleet Büro, Donnerstag 14:30 Uhr.
Code Sentinel: „Nova, warum ist deine Query so langsam? 500ms für 10 Benutzer?“
Nova: „Keine Ahnung, ich lade doch nur die Benutzer und ihre Bestellungen…“
Code Sentinel: „Zeig mal den Code.“
List<Person> personen = em.createQuery("SELECT p FROM Person p").getResultList();
for (Person p : personen) {
System.out.println(p.getName() + ": " + p.getBestellungen().size());
}
Code Sentinel: „Da ist dein Problem. Das N+1-Problem.“
Nova: „N+1?“
Code Sentinel: „Du machst 1 Query für die Personen. Dann für JEDE Person noch eine Query für die Bestellungen. 10 Personen = 11 Queries.“
Nova: „Oh… und mit 1000 Personen?“
Code Sentinel: „1001 Queries. Das wird nicht schneller. Die Lösung: JOIN FETCH.“
List<Person> personen = em.createQuery(
"SELECT DISTINCT p FROM Person p LEFT JOIN FETCH p.bestellungen",
Person.class
).getResultList();
Nova: „Eine Query statt 1001?“
Code Sentinel: „Genau. Merk dir: Wenn du über Collections iterierst, immer prüfen ob du ein N+1-Problem hast. Hibernate zeigt dir die Queries wenn show_sql=true ist.“
✅ Checkpoint
📝 Quiz
Frage 1: Was ist der Unterschied zwischen SQL und JPQL?
A) JPQL ist schneller
B) JPQL arbeitet mit Entity-Namen, SQL mit Tabellennamen
C) SQL kann mehr als JPQL
D) Es gibt keinen Unterschied
Frage 2: Was bedeutet mappedBy in @OneToMany(mappedBy = "person")?
A) Der Tabellenname
B) Das Feld in der ANDEREN Entity, das die Beziehung „besitzt“
C) Der Spaltenname für den Foreign Key
D) Die Mapping-Strategie
Frage 3: Was ist das N+1-Problem?
A) Eine Query gibt N+1 Ergebnisse zurück
B) Für N Datensätze werden N+1 Queries ausgeführt
C) Die Pagination funktioniert nicht
D) JOIN nicht möglich
Frage 4: Wie vermeidet man das N+1-Problem?
A) Mehr RAM
B) @Lazy Annotation
C) JOIN FETCH in der JPQL-Query
D) Kleinere Batches
📝 Quiz-Lösungen
Frage 1: ✅ B – JPQL arbeitet mit Entity-Namen
JPQL: SELECT p FROM Person p. SQL: SELECT * FROM personen.
Frage 2: ✅ B – Das Feld in der anderen EntitymappedBy = "person" bedeutet: „Die Bestellung-Entity hat ein Feld person, das diese Beziehung definiert.“
Frage 3: ✅ B – N+1 Queries werden ausgeführt
1 Query für die Hauptentität, dann N Queries für die verknüpften Entities.
Frage 4: ✅ C – JOIN FETCHSELECT p FROM Person p LEFT JOIN FETCH p.bestellungen lädt alles in einer Query.
❓ FAQ
Wann @OneToMany, wann @ManyToMany?
@OneToMany: Eine Bestellung gehört zu EINER Person. @ManyToMany: Ein Student kann in MEHREREN Kursen sein, ein Kurs hat MEHRERE Studenten.
Was ist CascadeType.ALL?
Operationen werden weitergegeben. persist(person) speichert auch die Adresse. remove(person) löscht auch die Bestellungen.
Was ist orphanRemoval = true?
Wenn eine Bestellung aus der Liste entfernt wird, wird sie auch aus der DB gelöscht.
JPQL oder Criteria API?
JPQL für 90% der Fälle. Criteria API nur wenn du dynamische Queries brauchst (z.B. Suchformulare mit optionalen Filtern).
Muss ich das alles selbst schreiben?
Nein! Spring Data JPA generiert alles automatisch. Das zeigen wir morgen in Tag 10!
📦 Downloads
Quick Start:
mvn exec:java # JPQL Demo mvn exec:java -Prelations # Relationship Demo mvn exec:java -Prepository # Repository Pattern
🔗 Weiterführende Links
| Ressource | Beschreibung |
|---|---|
| JPQL Reference | Offizielle Spezifikation |
| Hibernate N+1 | N+1 Problem im Detail |
| Spring Data JPA | Die Zukunft des Repository Patterns |
🎉 Tag 9 geschafft!
Was du heute gelernt hast:
✅ JPQL für objektorientierte Abfragen
✅ Entity Relationships (@OneToOne, @OneToMany, @ManyToOne)
✅ Das N+1-Problem und JOIN FETCH
✅ Repository Pattern mit JPA
✅ Wann JPQL, Criteria API oder Native SQL
Morgen – Tag 10: Integration & Ausblick (🔴 KOPFNUSS)
Der Abschluss! GUI + Datenbank zusammenbringen. Plus: Spring Boot Vorschau – wie das alles VIEL einfacher wird.
Fragen? franz-martin@java-developer.online
📚 Das könnte dich auch interessieren
© 2026 Java Fleet Systems Consulting | java-developer.online

