From cd22716bf191aa4ff38c198b657b373c15bbcbe8 Mon Sep 17 00:00:00 2001 From: Avgustin Marinov Date: Mon, 19 Jan 2026 10:07:03 +0200 Subject: [PATCH] [#2876] Temporary workaround of #2876 (#2874) Should be fixed with next Spring ORM - 7.0.4 / 6.2.16 Signed-off-by: Avgustin Marinov --- .../jpa/HawkbitEclipseLinkJpaDialect.java | 84 ++++++++++++------- ... => HawkbitEclipseLinkJpaDialectTest.java} | 2 +- 2 files changed, 53 insertions(+), 33 deletions(-) rename hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/test/java/org/eclipse/hawkbit/repository/jpa/{HawkBitEclipseLinkJpaDialectTest.java => HawkbitEclipseLinkJpaDialectTest.java} (98%) diff --git a/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkbitEclipseLinkJpaDialect.java b/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkbitEclipseLinkJpaDialect.java index 0c3996a01..845a37d70 100644 --- a/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkbitEclipseLinkJpaDialect.java +++ b/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkbitEclipseLinkJpaDialect.java @@ -10,63 +10,85 @@ package org.eclipse.hawkbit.repository.jpa; import java.io.Serial; +import java.lang.reflect.Field; +import java.sql.Connection; import java.sql.SQLException; +import java.util.concurrent.locks.ReentrantLock; +import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceException; +import lombok.extern.slf4j.Slf4j; import org.eclipse.hawkbit.repository.jpa.utils.JpaExceptionTranslator; import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.datasource.ConnectionHandle; import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator; import org.springframework.lang.NonNull; import org.springframework.orm.jpa.JpaSystemException; import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect; /** - * {@link EclipseLinkJpaDialect} with additional exception translation - * mechanisms based on {@link SQLStateSQLExceptionTranslator}. - * - * There are multiple variations of exceptions coming out of persistence - * provider: - * - *

- * 1) {@link PersistenceException}s that can be mapped by - * {@link EclipseLinkJpaDialect} into corresponding {@link DataAccessException}. - *

- * 2) {@link PersistenceException}s that could not be mapped by - * {@link EclipseLinkJpaDialect} directly but instead are wrapped into - * {@link JpaSystemException}. - *

- * 2.a) here the wrapped exception's causes might be an {@link SQLException} - * which might be mappable by {@link SQLStateSQLExceptionTranslator} or - *

- * 2.b.) the wrapped exception's causes due not contain an {@link SQLException} - * and as a result cannot be mapped. - *

- * 3) A {@link RuntimeException} that is no {@link PersistenceException}. - *

- * 3.a) here a cause might be an {@link SQLException} which might be mappable by - * {@link SQLStateSQLExceptionTranslator} or - *

- * 3.b.) the cause is not an {@link SQLException} and as a result cannot be - * mapped. + * {@link EclipseLinkJpaDialect} with additional exception translation mechanisms based on {@link SQLStateSQLExceptionTranslator}. + * There are multiple variations of exceptions coming out of persistence provider: + *

    + *
  1. {@link PersistenceException}s that can be mapped by {@link EclipseLinkJpaDialect} into corresponding {@link DataAccessException}
  2. + *
  3. {@link PersistenceException}s that could not be mapped by {@link EclipseLinkJpaDialect} directly but instead are wrapped into {@link JpaSystemException}. + *
      + *
    1. here the wrapped exception's causes might be an {@link SQLException} which might be mappable by {@link SQLStateSQLExceptionTranslator} or
    2. + *
    3. the wrapped exception's causes due not contain an {@link SQLException} and as a result cannot be mapped.
    4. + *
    + *
  4. + *
  5. A {@link RuntimeException} that is no {@link PersistenceException}. + *
      + *
    1. here a cause might be an {@link SQLException} which might be mappable by {@link SQLStateSQLExceptionTranslator} or
    2. + *
    3. the cause is not an {@link SQLException} and as a result cannot be mapped.
    4. + *
    + *
  6. + *
*/ +@Slf4j class HawkbitEclipseLinkJpaDialect extends EclipseLinkJpaDialect { @Serial private static final long serialVersionUID = 1L; + // TODO: switch to Spring fix - temporarily workaround of https://github.com/eclipse-hawkbit/hawkbit/issues/2876 + // (https://github.com/spring-projects/spring-framework/issues/36165) + private final ReentrantLock supperTransactionIsolationLock; + @SuppressWarnings("java:S3011") // temporarily - to workaround bug in supper class + HawkbitEclipseLinkJpaDialect() { + try { + final Field field = EclipseLinkJpaDialect.class.getDeclaredField("transactionIsolationLock"); + field.setAccessible(true); + supperTransactionIsolationLock = (ReentrantLock) field.get(this); + } catch (final NoSuchFieldException | IllegalAccessException e) { + log.error(e.getMessage(), e); + throw new IllegalStateException("Cannot access supper class field transactionIsolationLock", e); + } + } + // hawkbit uses JDBC and no need of lazy connection fetching + @Override + public ConnectionHandle getJdbcConnection(final EntityManager entityManager, final boolean readOnly) throws PersistenceException { + final Connection connection; + supperTransactionIsolationLock.lock(); + try { + connection = entityManager.unwrap(Connection.class); + } finally { + supperTransactionIsolationLock.unlock(); + } + return () -> connection; + } + @Override public DataAccessException translateExceptionIfPossible(@NonNull final RuntimeException ex) { final DataAccessException dataAccessException = super.translateExceptionIfPossible(ex); - if (dataAccessException == null) { return searchAndTranslateSqlException(ex); } return translateJpaSystemExceptionIfPossible(dataAccessException); } - private static DataAccessException translateJpaSystemExceptionIfPossible( - final DataAccessException accessException) { + private static DataAccessException translateJpaSystemExceptionIfPossible(final DataAccessException accessException) { if (!(accessException instanceof JpaSystemException)) { return accessException; } @@ -83,7 +105,6 @@ class HawkbitEclipseLinkJpaDialect extends EclipseLinkJpaDialect { if (sqlException == null) { return null; } - return JpaExceptionTranslator.getTranslator().translate("", null, sqlException); } @@ -96,7 +117,6 @@ class HawkbitEclipseLinkJpaDialect extends EclipseLinkJpaDialect { } exception = cause; } while (exception != null); - return null; } } \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/test/java/org/eclipse/hawkbit/repository/jpa/HawkBitEclipseLinkJpaDialectTest.java b/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/test/java/org/eclipse/hawkbit/repository/jpa/HawkbitEclipseLinkJpaDialectTest.java similarity index 98% rename from hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/test/java/org/eclipse/hawkbit/repository/jpa/HawkBitEclipseLinkJpaDialectTest.java rename to hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/test/java/org/eclipse/hawkbit/repository/jpa/HawkbitEclipseLinkJpaDialectTest.java index 4a99d4092..2b693bc26 100644 --- a/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/test/java/org/eclipse/hawkbit/repository/jpa/HawkBitEclipseLinkJpaDialectTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/test/java/org/eclipse/hawkbit/repository/jpa/HawkbitEclipseLinkJpaDialectTest.java @@ -28,7 +28,7 @@ import org.springframework.dao.UncategorizedDataAccessException; * Feature: Unit Tests - Repository
* Story: Exception handling */ -class HawkBitEclipseLinkJpaDialectTest { +class HawkbitEclipseLinkJpaDialectTest { private final HawkbitEclipseLinkJpaDialect hawkBitEclipseLinkJpaDialectUnderTest = new HawkbitEclipseLinkJpaDialect();