16 KiB
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
- Vor der ersten Ausführung wird eine Transaktion eröffnet.
- Der Zustand vor dem Start wird zwischengespeichert.
- Falls ein Fehler auftritt, markiert der Server die Transaktion als fehlerhaft.
- Falls alle Aktionen fehlerfrei durchlaufen → commit (zwischengespeicherte Zustände werden gelöscht).
- 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:
- Phase 1: Transaktionskoordinator veranlasst Ausführung bei allen beteiligten Systemen.
- 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
-
System-Exception (von
RuntimeExceptionoderjava.rmi.RemoteExceptionabgeleitet):- Wird vom Server in eine Application-Exception verpackt und an den Client geleitet.
- Rollback kann verhindert werden mit
@ApplicationExceptionan der Exception-Klasse. - Nach einer System-Exception in einer Stateful/Stateless Session Bean wird die Bean zerstört.
-
Application-Exception mit
@ApplicationException(rollback=true):- Application-Exceptions sind checked Exceptions (von
Exception, aber nicht vonRuntimeExceptionabgeleitet). - Werden direkt an den Client weitergeleitet (kein Verpacken).
- Checked Exceptions führen standardmäßig nicht zum Rollback.
- Application-Exceptions sind checked Exceptions (von
-
Manuell:
sessionContext.setRollbackOnly()(SessionContext per@Resourceinjizieren).
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_NEWundSUPPORTSmö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
- Projekt gemäß Anleitung anlegen (ejb- und war-Module im Temp-Ordner belassen).
- 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
@Componentzu Spring-managed Beans. - Müssen in einem Unterpaket der Klasse mit
@SpringBootApplicationliegen.
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,@Controllerim 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:
@PostConstructetc. 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 →
@Transactionalan 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) |