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:
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
|
|
||||||
Reference in New Issue
Block a user