Files
itc.componentware/docs/vorlesungen/06 - Transaktionen, Architektur, Packaging und Spring-Boot.md
T
2026-06-11 19:31:22 +02:00

16 KiB
Raw Blame History

Componentware Transaktionen, Packaging, Architektur und Spring Boot

Inhalt

Teil 1: Transaktionen & Packaging

  • Transaktionen: Einführung, CMT, BMT, Client Managed
  • Packaging
  • Architektur
  • Abschließendes Beispiel

Teil 2: Spring Boot

  • Überblick über Spring
  • Starter
  • Anlegen eines Spring-Boot Projekts
  • Spring Shell
  • Komponenten, CDI
  • Zugriff auf Datenbanken / Spring Data
  • Messaging
  • Spring Web MVC / RESTful Web Services
  • Transaktionen

Einführung

  • Eine der wichtigsten Aufgaben des Entwicklers von Enterprise Systemen stellt die Aufrechterhaltung der Systemkonsistenz dar.
  • Falls von einem Geschäftsprozess mehr als eine Aktion durchgeführt wird, muss sichergestellt sein, dass entweder alle Einzelschritte oder gar keiner der Schritte durchgeführt wird.
  • Beispiel: Bestellung → Bestand reduzieren + Spediteur benachrichtigen. Beide Schritte müssen zusammen ablaufen.
  • Das Jakarta Transactions API (JTA) stellt diverse Möglichkeiten der Transaktionsverwaltung bereit.

Ablauf einer Transaktion

  1. Vor der ersten Ausführung wird eine Transaktion eröffnet.
  2. Der Zustand vor dem Start wird zwischengespeichert.
  3. Falls ein Fehler auftritt, markiert der Server die Transaktion als fehlerhaft.
  4. Falls alle Aktionen fehlerfrei durchlaufen → commit (zwischengespeicherte Zustände werden gelöscht).
  5. Falls die Transaktion als ungültig markiert wurde → rollback (Zustand vor der Transaktion wird wiederhergestellt).

Der Transaktionskontext wird entlang der Aufrufkette propagiert (bei Lookups, Injections oder Proxy-Objekten).

ACID-Prinzip

Eigenschaft Bedeutung
Atomicity Alle Teilschritte oder keiner wird ausgeführt.
Consistency Nach der Ausführung ist der Datenbestand konsistent.
Isolation Mehrere Transaktionen beeinflussen sich nicht gegenseitig.
Durability Ergebnisse sind dauerhaft (überstehen z.B. Server-Neustart).

2-Phase-Commit

Bei verteilten Systemen erfolgt die Transaktion per 2-Phase-Commit:

  1. Phase 1: Transaktionskoordinator veranlasst Ausführung bei allen beteiligten Systemen.
  2. Phase 2: Bei Erfolg → finales Commit; bei Fehler → Rollback.

Transaktionsarten

  • CMT (Container Managed Transactions): Voreinstellung bei EJBs der Container übernimmt die Steuerung.
  • BMT (Bean Managed Transactions): Der Entwickler steuert die Transaktionen selbst.
  • Client Managed Transactions: Der Client startet die Transaktion.

Container Managed Transactions (CMT)

  • Annotierung der Klasse mit @TransactionManagement(TransactionManagementType.CONTAINER) (ist Voreinstellung).
  • Transaktionen starten beim Client-Aufruf einer Methode und enden i.d.R. mit der Methode.
  • JMS-Nachrichten oder Datenbankstatements werden erst am Ende der Transaktion versendet.

Rollback-Auslöser

  1. System-Exception (von RuntimeException oder java.rmi.RemoteException abgeleitet):

    • Wird vom Server in eine Application-Exception verpackt und an den Client geleitet.
    • Rollback kann verhindert werden mit @ApplicationException an der Exception-Klasse.
    • Nach einer System-Exception in einer Stateful/Stateless Session Bean wird die Bean zerstört.
  2. Application-Exception mit @ApplicationException(rollback=true):

    • Application-Exceptions sind checked Exceptions (von Exception, aber nicht von RuntimeException abgeleitet).
    • Werden direkt an den Client weitergeleitet (kein Verpacken).
    • Checked Exceptions führen standardmäßig nicht zum Rollback.
  3. Manuell: sessionContext.setRollbackOnly() (SessionContext per @Resource injizieren).


CMT @TransactionAttribute

@TransactionAttribute(TransactionAttributeType.<VALUE>)
Wert Bedeutung
REQUIRED (Standard) Vorhandenen Kontext verwenden oder neuen anlegen.
REQUIRES_NEW Immer neuen Kontext anlegen; vorhandener wird pausiert.
MANDATORY Vorhandener Kontext erforderlich; sonst TransactionRequiredException.
SUPPORTS Kontext wird übernommen, wenn vorhanden; sonst ohne Transaktion.
NOT_SUPPORTED Keinen Kontext verwenden; vorhandener wird unterbrochen.
NEVER Keinen Kontext akzeptieren; bei vorhandenem Kontext → EJBException.

Hinweis: Bei Message Driven Beans sind nur REQUIRED, REQUIRES_NEW und SUPPORTS möglich.

this-Aufrufproblem

Ruft eine EJB-Methode eine andere Methode der gleichen Klasse über this auf, registriert der Container diesen Aufruf nicht → das Transaktionsattribut der aufgerufenen Methode wird ignoriert.

Lösung: Aufruf über eine andere Bean (per @EJB injiziert) oder über sessionContext.getBusinessObject(KlassenName.class).


CMT Container Callback Methoden (Session Synchronisierung)

Für Stateful Session Beans, die länger als eine Transaktion leben. Interface: SessionSynchronization (oder Annotationen):

Methode / Annotation Zeitpunkt
afterBegin() / @AfterBegin Nach Beginn der Transaktion
beforeCompletion() / @BeforeCompletion In Phase 1 des 2-Phase-Commits
afterCompletion(boolean commit) / @AfterCompletion In Phase 2 (true = commit, false = rollback)

Bean Managed Transactions (BMT)

@TransactionManagement(TransactionManagementType.BEAN)

Zugriff auf UserTransaction über sessionContext.getUserTransaction().

UserTransaction-Methoden

Methode Beschreibung
begin() Startet eine neue Transaktion. BMT kann nicht an propagierten Transaktionen teilnehmen.
commit() Beendet und bestätigt die Transaktion.
rollback() Beendet und verwirft die Transaktion.

Achtung: In Stateful Session Beans können Transaktionen über mehrere Methoden geöffnet bleiben Transaktion muss in jedem Fall beendet werden.


Client Managed Transactions

InitialContext ctx = new InitialContext(..);
UserTransaction tx = (UserTransaction) ctx.lookup("UserTransaction");

Steuerung über dieselben Methoden wie bei BMT: begin(), commit(), rollback().

Der Jakarta EE Standard empfiehlt Serverherstellern, Client-Transaktionen zu unterstützen es ist jedoch keine Pflicht.


Packaging

Archivtyp Inhalt Verwendung
.jar EJB-Projekt EJB Application Archive
.war Web-Projekt Web Application Archive
.ear jar + war + Deployment-Deskriptor Enterprise Archive

EAR mit IntelliJ erstellen

  1. Projekt gemäß Anleitung anlegen (ejb- und war-Module im Temp-Ordner belassen).
  2. Unter Artifacts → „Java EE Application: Archive" erstellen:
    • Output Directory = WildFly Deploy-Verzeichnis
    • „Include in project build" anhaken
    • EJB- und WAR-Artifact per Drag & Drop hinzufügen

Architektur

Klassische 3-Schichten-Architektur

Bedieneroberfläche → Fachkonzept → Datenhaltung

Mit Web-Controller (MVC):

Bedieneroberfläche → Controller → Fachkonzept → Datenhaltung

Mit Jakarta EE Middleware:

Bedieneroberfläche → Controller → Middleware (EJBs / DAOs) → Datenhaltung

Data Transfer Objects (DTOs)

  • Ohne DTOs: Entities werden direkt zum Client transportiert.
  • Mit DTOs: Auf den Client angepasste Klassen → bessere Sicherheit und Performance.

Monolithische Applikationen vs. Microservices

Monolithen

Vorteile Nachteile
Einfache Entwicklung, IDE-Unterstützung Schwer verständlich bei großer Codebasis
Einfaches Deployment (eine Datei) Skalierung nur für das Gesamtsystem
Einfache End-to-End-Tests Hohe Compilezeit
Leicht mit Load Balancer skalierbar Lange Start- und Testzeiten

Microservices

Vorteile Nachteile
Kleinere, besser verständliche Codebasis Schwierige Trennungsfindung
Einzeln deploybar und skalierbar Netzwerkkomplexität
Teams können autonom arbeiten Übergreifende Transaktionen schwierig
Verschiedene Technologien möglich Höherer Administrationsaufwand
Schnellere Tests pro Service Manche Aufgaben je Service implementieren

Das Spring Boot Framework

Überblick

  • Spring ist ein Entwicklungsframework für Enterprise Applikationen auf Java-Basis (Alternative zu Jakarta EE).
  • Erste Version: 2004; seit 2009 durch VMware entwickelt.
  • Spring Boot (ab 2014): deutlich vereinfachte Konfiguration durch Autokonfiguration.
  • Projekte anlegen über https://start.spring.io oder IntelliJ.

Spring vs. Spring Boot

Spring Spring Boot
Container muss selbst konfiguriert werden Sinnvolle Voreinstellungen
Kein embedded Server Embedded Apache Tomcat

Typische Module

  • Spring Data Datenbankzugriff
  • Spring Web Webapplikationen inkl. RESTful Web Services
  • Spring Security Authentifizierung und Autorisierung
  • Spring Messaging Messaging
  • Spring Cloud Cloud-Technologien (AWS, Azure, ...)

Starter

Ein Starter ist eine Maven-Dependency, die alle für eine Technologie notwendigen Libraries lädt und eine Autokonfiguration vornimmt.


Anlegen eines Spring-Boot-Projekts

Hauptklasse

@SpringBootApplication
public class SpringDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringDemoApplication.class, args);
    }
}

Interaktive Anwendungen mit der Spring Shell

Starter:

<dependency>
    <groupId>org.springframework.shell</groupId>
    <artifactId>spring-shell-starter</artifactId>
</dependency>

Shell-Methode erstellen:

@Component
public class HelloWorld {
    @Command(description = "hello: Says Hello World!")
    public String hello() {
        return "Hello World!";
    }
}

Aufruf im Prompt (Methodenname in kebab-case):

$> hello
Hello World!

Mit Parameter:

@Command(description="hello-to --name <name>: ...")
public String helloTo(@Option(required = true) String name) {
    return "Hello " + name;
}
$> hello-to --name Petra
Hello Petra

Hinweis: In neueren IntelliJ-Versionen muss die App über Maven gestartet werden (./mvnw spring-boot:run).


Komponenten

  • POJOs werden mit @Component zu Spring-managed Beans.
  • Müssen in einem Unterpaket der Klasse mit @SpringBootApplication liegen.

Dependency Injection

@Autowired
private SomeManagedBean bean; // Attribut-Injection

Besser: Konstruktor-Injection (final-Attribute möglich, testbar ohne Spring-Kontext):

@Autowired
public HelloWorld(HelloWorldComponent helloWorldComponent) {
    this.helloWorldComponent = helloWorldComponent;
}

Semantische Annotationen

Annotation Verwendung
@Component Allgemeine Spring-managed Bean
@Service Service-Klassen (Fachlogik)
@Repository Data Access Objects
@Controller / @RestController Web-Controller

CDI in Spring

  • Component Scanning: Spring sucht nach @Component, @Repository, @Service, @Controller im Paket der Hauptklasse und Unterpaketen.
  • @Bean: Entspricht CDI-Producern macht Werte aus Methoden injizierbar.
@Bean
public String name() {
    return "Max Mustermann";
}
  • @ComponentScan: Erweitert den Suchpfad.
  • Interceptoren: @PostConstruct etc. wie in Jakarta EE. Alternativ AOP.

Zugriff auf Datenbanken

Spring Data JPA

Starter:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

application.properties:

spring.datasource.url=jdbc:mysql://localhost:3306/dbname
spring.datasource.username=<username>
spring.datasource.password=<password>
spring.jpa.hibernate.ddl-auto=create-drop
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect

In Spring laufen Methoden nicht automatisch transaktional@Transactional an Methoden oder Klassen notwendig.

SimpleJpaRepository

SimpleJpaRepository<Book, Long> repository = new SimpleJpaRepository<>(Book.class, em);
List<Book> books = repository.findAll();

CrudRepository / ListCrudRepository / JpaRepository

public interface BookJpaRepository extends JpaRepository<Book, Long> {
    // Methoden werden automatisch generiert
}

Kein @Autowired nötig Spring erkennt das Interface und erstellt automatisch eine Implementierung.


Messaging

Starter (ActiveMQ):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>

ActiveMQ als Docker Container starten:

docker run -d --name activemq -p 61616:61616 -p 8161:8161 rmohr/activemq

application.properties:

spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.user=admin
spring.activemq.password=admin
app.queue.name=buch.queue
spring.jms.pub-sub-domain=false

Hauptklasse:

@SpringBootApplication
@EnableJms
public class MyApp { ... }

Receiver

@Component
public class MessageReceiver {
    @JmsListener(destination = "springQueue")
    public void receiveMessage(Book receivedBook, Message message) {
        // Verarbeitung
    }
}

Sender

JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);
jmsTemplate.convertAndSend("queueName", new Book("Buch", 42.99));

MessageConverter (für benutzerdefinierte Typen via JSON/Jackson)

@Bean
public MessageConverter jacksonJmsMessageConverter() {
    JacksonJsonMessageConverter converter = new JacksonJsonMessageConverter();
    converter.setTypeIdPropertyName("_type");
    Map<String, Class<?>> typeIdMappings = new HashMap<>();
    typeIdMappings.put("buch", Buch.class);
    converter.setTypeIdMappings(typeIdMappings);
    return converter;
}

Spring Web MVC RESTful Web Services

Starter:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Controller

@RestController
@RequestMapping("/hello")
public class HelloWebService {
    @GetMapping
    public String sayHello() {
        return "Hello from Web Service";
    }
}

Mapping-Annotationen

Annotation HTTP-Methode
@GetMapping GET
@PostMapping POST
@PutMapping PUT
@DeleteMapping DELETE

Parameter

Query-Parameter:

@GetMapping("/to")
public String sayHelloTo(@RequestParam(value = "name", defaultValue = "World") String name) {
    return "Hello to " + name;
}
// Aufruf: http://localhost:8080/hello/to?name=Peter

Path-Parameter:

@GetMapping("/to/{name}")
public String sayHelloToParam(@PathVariable String name) {
    return "Hello to " + name;
}

Rückgabe von Objekten

@GetMapping("/book/{id}")
public ResponseEntity<?> getBookById(@PathVariable long id) {
    Book book = bookJpaRepository.getReferenceById(id);
    return ResponseEntity.ok(book); // automatisch via JSON
}

Empfang von Objekten

@PostMapping(path = "/book")
public ResponseEntity<?> saveBook(@RequestBody Book book) {
    Book b = bookJpaRepository.save(book);
    return ResponseEntity.ok(b);
}

Transaktionen in Spring

Deklarativ (@Transactional)

@Transactional
public void doSomething() {
    // läuft in einem Transaktionskontext ab
}
  • Bei RuntimeException → automatischer Rollback.
  • Konfigurierbar: readOnly, Isolation (REQUIRED, REQUIRES_NEW), welche Exceptions zum Rollback führen.
  • Neuer Thread erhält keinen propagierten Transaktionskontext.

Programmgesteuert

@Service
public class DoSomethingService {
    @Autowired
    private PlatformTransactionManager transactionManager;

    public void doSomething() {
        TransactionDefinition definition = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(definition);
        try {
            // ... Datenbankoperationen ...
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
        }
    }
}

Kernklassen

Klasse Rolle
PlatformTransactionManager Führt Transaktionen technisch aus (begin, commit, rollback)
TransactionDefinition Beschreibt Eigenschaften (Propagation, Timeout, ReadOnly, Name)
TransactionStatus Repräsentiert die laufende Transaktion (Status abfragbar)