Manuelles JDBC Commit/Rollback
Die Service-Methode öffnet die Connection und steuert Commit, Rollback und Auto-Commit selbst.
Vollständiger Legacy-Modern-Vergleich aller Java-Klassen, Tests, Maven-POMs und relevanten Ressourcen dieses Moduls. Jede Datei ist genau einmal einer fachlich passenden Vergleichsgruppe zugeordnet.
Die Service-Methode öffnet die Connection und steuert Commit, Rollback und Auto-Commit selbst.
Die Service-Methode beschreibt die Transaktionsgrenze deklarativ; das Repository übernimmt den Datenzugriff.
Die Ähnlichkeit ist eine technische Orientierung auf normalisiertem Quelltext. Sie ersetzt keine fachliche Bewertung.
| Vergleichsgruppe | Legacy-Dateien | Modern-Dateien | Legacy-Zeilen | Modern-Zeilen | Ähnlichkeit |
|---|---|---|---|---|---|
| Build-Konfiguration | 1 | 1 | 35 | 43 | 56% |
| Bootstrap und Konfiguration | 1 | 2 | 15 | 13 | 30% |
| Transfer-Use-Case und Test | 2 | 2 | 85 | 44 | 16% |
| Persistenzabstraktion | 0 | 1 | 0 | 23 | 0% |
Das Pair-POM bindet die Legacy- und Modern-Submodule zusammen und gehört deshalb ebenfalls zur vollständigen Codeabdeckung.
Transaktions- und Persistenzabhängigkeiten werden verglichen.
| Legacy-Dateien | Modern-Dateien | Einordnung |
|---|---|---|
| pom.xml | pom.xml | Direktvergleich Semantische Ähnlichkeit: 56% |
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>at.aydin.academy</groupId>
<artifactId>05transactions-pair</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>transactions-legacy</artifactId>
<packaging>jar</packaging>
<name>Transactions Legacy (JDBC)</name>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project><?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>at.aydin.academy</groupId>
<artifactId>05transactions-pair</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>transactions-modern</artifactId>
<packaging>jar</packaging>
<name>Transactions Modern (Spring)</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>Manuelles Datenbanksetup wird durch Boot-Konfiguration ersetzt.
| Legacy-Dateien | Modern-Dateien | Einordnung |
|---|---|---|
| TransactionsLegacyApplication.java | TransactionsModernApplication.java application.properties | Direktvergleich Semantische Ähnlichkeit: 30% |
package at.aydin.lab.transactions.legacy;
import java.math.BigDecimal;
public final class TransactionsLegacyApplication {
private TransactionsLegacyApplication() {
}
public static void main(String[] args) {
LegacyTransferService service = new LegacyTransferService("jdbc:h2:mem:txlegacy;DB_CLOSE_DELAY=-1");
service.transfer(1, 2, new BigDecimal("50.00"), false);
System.out.println("Konto 1: " + service.balance(1));
System.out.println("Konto 2: " + service.balance(2));
}
}package at.aydin.lab.transactions.modern;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class TransactionsModernApplication {
public static void main(String[] args) {
SpringApplication.run(TransactionsModernApplication.class, args);
}
}spring.datasource.url=jdbc:h2:mem:txmodern;DB_CLOSE_DELAY=-1
spring.sql.init.mode=alwaysExplizite JDBC-Transaktion wird mit deklarativer Transaktion verglichen.
| Legacy-Dateien | Modern-Dateien | Einordnung |
|---|---|---|
| LegacyTransferService.java LegacyTransferServiceTest.java | TransferService.java TransferServiceTest.java | Direktvergleich Semantische Ähnlichkeit: 16% |
package at.aydin.lab.transactions.legacy;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
// Design Pattern: Transaction Script
// Zweck: Ein kompletter fachlicher Ablauf wird explizit in einer Methode transaktional ausgeführt.
public final class LegacyTransferService {
private final String url;
public LegacyTransferService(String url) {
this.url = url;
initialize();
}
private Connection connection() throws SQLException {
return DriverManager.getConnection(url, "sa", "");
}
private void initialize() {
try (Connection c = connection(); Statement s = c.createStatement()) {
s.executeUpdate("create table if not exists account(id bigint primary key, balance decimal(15,2))");
s.executeUpdate("merge into account key(id) values (1,1000.00),(2,200.00)");
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}
public void transfer(long from, long to, BigDecimal amount, boolean simulateFailure) {
try (Connection c = connection()) {
c.setAutoCommit(false);
try {
update(c, from, amount.negate());
if (simulateFailure) throw new IllegalStateException("Simulierter Fehler");
update(c, to, amount);
c.commit();
} catch (Exception e) {
c.rollback();
throw new IllegalStateException("Überweisung zurückgerollt", e);
}
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}
private void update(Connection c, long id, BigDecimal delta) throws SQLException {
try (PreparedStatement ps = c.prepareStatement("update account set balance=balance+? where id=?")) {
ps.setBigDecimal(1, delta);
ps.setLong(2, id);
if (ps.executeUpdate() != 1) throw new SQLException("Konto fehlt: " + id);
}
}
public BigDecimal balance(long id) {
try (Connection c = connection(); PreparedStatement ps = c.prepareStatement("select balance from account where id=?")) {
ps.setLong(1, id);
try (ResultSet rs = ps.executeQuery()) {
if (!rs.next()) throw new IllegalArgumentException();
return rs.getBigDecimal(1);
}
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}
}package at.aydin.lab.transactions.legacy;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class LegacyTransferServiceTest {
@Test
void rollsBackBothUpdates() {
LegacyTransferService service = new LegacyTransferService("jdbc:h2:mem:txrollback;DB_CLOSE_DELAY=-1");
assertThrows(IllegalStateException.class, () -> service.transfer(1, 2, new BigDecimal("50.00"), true));
assertEquals(new BigDecimal("1000.00"), service.balance(1));
assertEquals(new BigDecimal("200.00"), service.balance(2));
}
}package at.aydin.lab.transactions.modern;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
@Service
// Design Pattern: Service Layer + declarative Transaction Boundary
// Zweck: Rollback-Regeln werden vom Framework statt durch manuelles JDBC gesteuert.
public class TransferService {
private final AccountRepository repository;
public TransferService(AccountRepository repository) {
this.repository = repository;
}
@Transactional
public void transfer(long from, long to, BigDecimal amount, boolean simulateFailure) {
repository.changeBalance(from, amount.negate());
if (simulateFailure) throw new IllegalStateException("Simulierter Fehler");
repository.changeBalance(to, amount);
}
}package at.aydin.lab.transactions.modern;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.math.BigDecimal;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@SpringBootTest
class TransferServiceTest {
@Autowired
TransferService service;
@Autowired
AccountRepository repository;
@Test
void rollsBackOnRuntimeException() {
assertThrows(IllegalStateException.class, () -> service.transfer(1, 2, new BigDecimal("50.00"), true));
assertEquals(new BigDecimal("1000.00"), repository.balance(1));
assertEquals(new BigDecimal("200.00"), repository.balance(2));
}
}Repository wird als neue, getrennte Datenzugriffsschicht eingeführt.
| Legacy-Dateien | Modern-Dateien | Einordnung |
|---|---|---|
| — | AccountRepository.java | Neu in Modern Semantische Ähnlichkeit: 0% |
package at.aydin.lab.transactions.modern;
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
@Repository
public class AccountRepository {
private final JdbcClient jdbc;
public AccountRepository(JdbcClient jdbc) {
this.jdbc = jdbc;
}
public void changeBalance(long id, BigDecimal delta) {
int count = jdbc.sql("update account set balance=balance+:delta where id=:id").param("delta", delta).param("id", id)
.update();
if (count != 1) throw new IllegalArgumentException("Konto fehlt: " + id);
}
public BigDecimal balance(long id) {
return jdbc.sql("select balance from account where id=:id").param("id", id).query(BigDecimal.class).single();
}
}