From c137d9e02edad79ac1e5816959fa7fed007502a9 Mon Sep 17 00:00:00 2001 From: Marcus van Elst Date: Tue, 28 Apr 2026 15:52:21 +0200 Subject: [PATCH] Fix #61: Connection-Leak in SqliteUnitOfWorkAdapter beheben Connection wird jetzt in try-with-resources geoeffnet, sodass sie auch dann zuverlaessig geschlossen wird, wenn setAutoCommit(false) wirft. Rollback-Behandlung bleibt unveraendert innerhalb des inneren catch-Blocks. Ebenfalls: korrekten Import fuer DateTimeFormatter ergaenzt. Co-Authored-By: Claude Sonnet 4.6 --- ...iteProcessingAttemptRepositoryAdapter.java | 2 +- .../out/sqlite/SqliteUnitOfWorkAdapter.java | 83 +++++++++---------- 2 files changed, 39 insertions(+), 46 deletions(-) diff --git a/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/sqlite/SqliteProcessingAttemptRepositoryAdapter.java b/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/sqlite/SqliteProcessingAttemptRepositoryAdapter.java index 98e8e89..b53d86f 100644 --- a/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/sqlite/SqliteProcessingAttemptRepositoryAdapter.java +++ b/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/sqlite/SqliteProcessingAttemptRepositoryAdapter.java @@ -7,7 +7,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; -import java.time.DateTimeFormatter; +import java.time.format.DateTimeFormatter; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; diff --git a/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/sqlite/SqliteUnitOfWorkAdapter.java b/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/sqlite/SqliteUnitOfWorkAdapter.java index d113ac8..5e3fd20 100644 --- a/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/sqlite/SqliteUnitOfWorkAdapter.java +++ b/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/sqlite/SqliteUnitOfWorkAdapter.java @@ -41,58 +41,51 @@ public class SqliteUnitOfWorkAdapter implements UnitOfWorkPort { public void executeInTransaction(Consumer operations) { Objects.requireNonNull(operations, "operations must not be null"); - Connection connection = null; - try { - connection = DriverManager.getConnection(jdbcUrl); + try (Connection connection = DriverManager.getConnection(jdbcUrl)) { connection.setAutoCommit(false); - TransactionOperationsImpl txOps = new TransactionOperationsImpl(connection); - operations.accept(txOps); + try { + TransactionOperationsImpl txOps = new TransactionOperationsImpl(connection); + operations.accept(txOps); - connection.commit(); - logger.debug("Transaction committed successfully"); + connection.commit(); + logger.debug("Transaktion erfolgreich abgeschlossen."); + + } catch (DocumentPersistenceException e) { + // Datenbankfehler auf Dokumentebene: Rollback, dann weiterpropagieren + try { + connection.rollback(); + logger.debug("Transaktion zurückgerollt (Dokumentfehler): {}", e.getMessage()); + } catch (SQLException rollbackEx) { + logger.error("Rollback fehlgeschlagen: {}", rollbackEx.getMessage(), rollbackEx); + } + throw e; + } catch (RuntimeException e) { + // Unerwarteter Laufzeitfehler: Rollback, dann als Persistenzfehler weitergeben + try { + connection.rollback(); + logger.debug("Transaktion zurückgerollt (Laufzeitfehler): {}", e.getMessage()); + } catch (SQLException rollbackEx) { + logger.error("Rollback fehlgeschlagen: {}", rollbackEx.getMessage(), rollbackEx); + } + throw new DocumentPersistenceException("Transaktion fehlgeschlagen: " + e.getMessage(), e); + } catch (SQLException e) { + // SQL-Fehler innerhalb der Transaktion: Rollback, dann als Persistenzfehler weitergeben + try { + connection.rollback(); + logger.debug("Transaktion zurückgerollt (SQL-Fehler): {}", e.getMessage()); + } catch (SQLException rollbackEx) { + logger.error("Rollback fehlgeschlagen: {}", rollbackEx.getMessage(), rollbackEx); + } + throw new DocumentPersistenceException("Transaktion fehlgeschlagen: " + e.getMessage(), e); + } } catch (DocumentPersistenceException e) { - // Re-throw document-level persistence errors as-is, but still rollback - if (connection != null) { - try { - connection.rollback(); - logger.debug("Transaction rolled back due to document error: {}", e.getMessage()); - } catch (SQLException rollbackEx) { - logger.error("Failed to rollback transaction: {}", rollbackEx.getMessage(), rollbackEx); - } - } throw e; - } catch (RuntimeException e) { - // Rollback on any RuntimeException and wrap in DocumentPersistenceException - if (connection != null) { - try { - connection.rollback(); - logger.debug("Transaction rolled back due to error: {}", e.getMessage()); - } catch (SQLException rollbackEx) { - logger.error("Failed to rollback transaction: {}", rollbackEx.getMessage(), rollbackEx); - } - } - throw new DocumentPersistenceException("Transaction failed: " + e.getMessage(), e); } catch (SQLException e) { - // Rollback for any SQL error - if (connection != null) { - try { - connection.rollback(); - logger.debug("Transaction rolled back due to error: {}", e.getMessage()); - } catch (SQLException rollbackEx) { - logger.error("Failed to rollback transaction: {}", rollbackEx.getMessage(), rollbackEx); - } - } - throw new DocumentPersistenceException("Transaction failed: " + e.getMessage(), e); - } finally { - if (connection != null) { - try { - connection.close(); - } catch (SQLException e) { - logger.warn("Failed to close connection: {}", e.getMessage(), e); - } - } + // Verbindungsaufbau oder setAutoCommit(false) fehlgeschlagen + throw new DocumentPersistenceException( + "Datenbankverbindung konnte nicht hergestellt werden: " + e.getMessage(), e); } }