⌂ Index
Kapitel 07 · Data

Spring Data JDBC für klare Aggregate

Typ: LibraryVersion 2 ausführlich

Spring Data JDBC ist eine schlankere, explizitere Alternative zu JPA für relationale Aggregate ohne komplexen Persistence Context.

Fachliche Einordnung

Es eignet sich, wenn du SQL-nahe Kontrolle, einfache Aggregate und weniger Hibernate-Magie möchtest. Änderungen sind direkter und oft leichter zu erklären.

Enterprise-Merksatz: Spring Data JDBC ist eine schlankere, explizitere Alternative zu JPA für relationale Aggregate ohne komplexen Persistence Context.

Technische Darstellung

Aggregate Repository JdbcAggregateTemplate SQL Database Transaction
Kernkonzepte
  • Aggregate werden vollständig geladen und gespeichert.
  • Kein Lazy Loading und kein First-Level-Cache wie bei JPA.
  • Repository-Abstraktion mit JDBC-Mapping.
  • @MappedCollection für Kindobjekte.
  • SQL bleibt besser sichtbar und kontrollierbar.
Wann einsetzen?
  • Du willst weniger ORM-Komplexität.
  • Dein Datenmodell ist relational, aber fachlich eher aggregate-orientiert.
  • Du willst Verhalten gut vorhersagen und SQL leichter optimieren.
Typische Fehler und Risiken
  • Zu große Aggregate verursachen teure Writes.
  • JPA-Denken wie Lazy Loading funktioniert hier nicht.
  • Komplexe Legacy-Schema-Relationen können aufwändig sein.
Legacy- und Modernisierungssicht

Für alte JDBC-DAOs ist Spring Data JDBC oft ein natürlicherer Zwischenschritt als JPA, weil SQL-Nähe und Transaktionsdenken erhalten bleiben.

Ausführliches Beispiel

Das Beispiel zeigt bewusst nicht nur Annotationen, sondern auch die Verantwortung der Schicht. In echten Projekten sollte der technische Spring-Code an Adapter- oder Konfigurationsrändern bleiben, während die Fachlogik testbar und möglichst frameworkarm bleibt.

@Table("purchase_order")
public class PurchaseOrder {
    @Id private UUID id;
    private String customerNumber;
    private OrderStatus status;

    @MappedCollection(idColumn = "order_id")
    private Set<PurchaseOrderLine> lines = new LinkedHashSet<>();

    public void addLine(String sku, int quantity) {
        if (status != OrderStatus.DRAFT) throw new IllegalStateException("order locked");
        lines.add(new PurchaseOrderLine(sku, quantity));
    }
}

interface PurchaseOrderJdbcRepository extends CrudRepository<PurchaseOrder, UUID> {}

@Service
class DraftOrderService {
    @Transactional
    public UUID createDraft(String customer) {
        return repository.save(PurchaseOrder.draft(customer)).getId();
    }
}
Checkliste für Reviews
  • Ist die Verantwortung des Bausteins klar: Framework steuert Lebenszyklus, Library wird gezielt benutzt?
  • Ist die Fachlogik außerhalb von Controller, Listener, Repository-Implementierung oder Konfiguration?
  • Sind Fehlerfälle, Timeouts, Security, Monitoring und Tests sichtbar modelliert?
  • Gibt es klare Grenzen zwischen DTO, Domäne, Persistence und Infrastruktur?