[#2876] Temporary workaround of #2876 (#2874)

Should be fixed with next Spring ORM - 7.0.4 / 6.2.16

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2026-01-19 10:07:03 +02:00
committed by GitHub
parent 2b72393309
commit cd22716bf1
2 changed files with 53 additions and 33 deletions

View File

@@ -10,63 +10,85 @@
package org.eclipse.hawkbit.repository.jpa; package org.eclipse.hawkbit.repository.jpa;
import java.io.Serial; import java.io.Serial;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.concurrent.locks.ReentrantLock;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceException; import jakarta.persistence.PersistenceException;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.jpa.utils.JpaExceptionTranslator; import org.eclipse.hawkbit.repository.jpa.utils.JpaExceptionTranslator;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.datasource.ConnectionHandle;
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator; import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.orm.jpa.JpaSystemException; import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect; import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
/** /**
* {@link EclipseLinkJpaDialect} with additional exception translation * {@link EclipseLinkJpaDialect} with additional exception translation mechanisms based on {@link SQLStateSQLExceptionTranslator}.
* mechanisms based on {@link SQLStateSQLExceptionTranslator}. * There are multiple variations of exceptions coming out of persistence provider:
* * <ol>
* There are multiple variations of exceptions coming out of persistence * <li>{@link PersistenceException}s that can be mapped by {@link EclipseLinkJpaDialect} into corresponding {@link DataAccessException}</li>
* provider: * <li>{@link PersistenceException}s that could not be mapped by {@link EclipseLinkJpaDialect} directly but instead are wrapped into {@link JpaSystemException}.
* * <ol>
* <p> * <li>here the wrapped exception's causes might be an {@link SQLException} which might be mappable by {@link SQLStateSQLExceptionTranslator} or </li>
* 1) {@link PersistenceException}s that can be mapped by * <li>the wrapped exception's causes due not contain an {@link SQLException} and as a result cannot be mapped. </li>
* {@link EclipseLinkJpaDialect} into corresponding {@link DataAccessException}. * </ol>
* <p> * </li>
* 2) {@link PersistenceException}s that could not be mapped by * <li>A {@link RuntimeException} that is no {@link PersistenceException}.
* {@link EclipseLinkJpaDialect} directly but instead are wrapped into * <ol>
* {@link JpaSystemException}. * <li>here a cause might be an {@link SQLException} which might be mappable by {@link SQLStateSQLExceptionTranslator} or </li>
* <p> * <li>the cause is not an {@link SQLException} and as a result cannot be mapped.</li>
* 2.a) here the wrapped exception's causes might be an {@link SQLException} * </ol>
* which might be mappable by {@link SQLStateSQLExceptionTranslator} or * </li>
* <p> * </ol>
* 2.b.) the wrapped exception's causes due not contain an {@link SQLException}
* and as a result cannot be mapped.
* <p>
* 3) A {@link RuntimeException} that is no {@link PersistenceException}.
* <p>
* 3.a) here a cause might be an {@link SQLException} which might be mappable by
* {@link SQLStateSQLExceptionTranslator} or
* <p>
* 3.b.) the cause is not an {@link SQLException} and as a result cannot be
* mapped.
*/ */
@Slf4j
class HawkbitEclipseLinkJpaDialect extends EclipseLinkJpaDialect { class HawkbitEclipseLinkJpaDialect extends EclipseLinkJpaDialect {
@Serial @Serial
private static final long serialVersionUID = 1L; 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 @Override
public DataAccessException translateExceptionIfPossible(@NonNull final RuntimeException ex) { public DataAccessException translateExceptionIfPossible(@NonNull final RuntimeException ex) {
final DataAccessException dataAccessException = super.translateExceptionIfPossible(ex); final DataAccessException dataAccessException = super.translateExceptionIfPossible(ex);
if (dataAccessException == null) { if (dataAccessException == null) {
return searchAndTranslateSqlException(ex); return searchAndTranslateSqlException(ex);
} }
return translateJpaSystemExceptionIfPossible(dataAccessException); return translateJpaSystemExceptionIfPossible(dataAccessException);
} }
private static DataAccessException translateJpaSystemExceptionIfPossible( private static DataAccessException translateJpaSystemExceptionIfPossible(final DataAccessException accessException) {
final DataAccessException accessException) {
if (!(accessException instanceof JpaSystemException)) { if (!(accessException instanceof JpaSystemException)) {
return accessException; return accessException;
} }
@@ -83,7 +105,6 @@ class HawkbitEclipseLinkJpaDialect extends EclipseLinkJpaDialect {
if (sqlException == null) { if (sqlException == null) {
return null; return null;
} }
return JpaExceptionTranslator.getTranslator().translate("", null, sqlException); return JpaExceptionTranslator.getTranslator().translate("", null, sqlException);
} }
@@ -96,7 +117,6 @@ class HawkbitEclipseLinkJpaDialect extends EclipseLinkJpaDialect {
} }
exception = cause; exception = cause;
} while (exception != null); } while (exception != null);
return null; return null;
} }
} }

View File

@@ -28,7 +28,7 @@ import org.springframework.dao.UncategorizedDataAccessException;
* Feature: Unit Tests - Repository<br/> * Feature: Unit Tests - Repository<br/>
* Story: Exception handling * Story: Exception handling
*/ */
class HawkBitEclipseLinkJpaDialectTest { class HawkbitEclipseLinkJpaDialectTest {
private final HawkbitEclipseLinkJpaDialect hawkBitEclipseLinkJpaDialectUnderTest = new HawkbitEclipseLinkJpaDialect(); private final HawkbitEclipseLinkJpaDialect hawkBitEclipseLinkJpaDialectUnderTest = new HawkbitEclipseLinkJpaDialect();