Skip to content

Commit e76b453

Browse files
committed
Translate Hibernate TransactionException through SQLException cause
Closes gh-31274
1 parent eafbcfd commit e76b453

File tree

3 files changed

+31
-6
lines changed

3 files changed

+31
-6
lines changed

spring-orm/src/main/java/org/springframework/orm/jpa/JpaTransactionManager.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,10 @@ protected void doRollback(DefaultTransactionStatus status) {
589589
}
590590
}
591591
catch (PersistenceException ex) {
592+
DataAccessException dae = getJpaDialect().translateExceptionIfPossible(ex);
593+
if (dae != null) {
594+
throw dae;
595+
}
592596
throw new TransactionSystemException("Could not roll back JPA transaction", ex);
593597
}
594598
finally {

spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaDialect.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.springframework.dao.PessimisticLockingFailureException;
6060
import org.springframework.jdbc.datasource.ConnectionHandle;
6161
import org.springframework.jdbc.datasource.DataSourceUtils;
62+
import org.springframework.jdbc.support.SQLExceptionSubclassTranslator;
6263
import org.springframework.jdbc.support.SQLExceptionTranslator;
6364
import org.springframework.lang.Nullable;
6465
import org.springframework.orm.ObjectOptimisticLockingFailureException;
@@ -74,7 +75,7 @@
7475

7576
/**
7677
* {@link org.springframework.orm.jpa.JpaDialect} implementation for Hibernate.
77-
* Compatible with Hibernate ORM 5.5/5.6 as well as 6.0/6.1/6.2.
78+
* Compatible with Hibernate ORM 5.5/5.6 as well as 6.0/6.1/6.2/6.3.
7879
*
7980
* @author Juergen Hoeller
8081
* @author Costin Leau
@@ -91,6 +92,9 @@ public class HibernateJpaDialect extends DefaultJpaDialect {
9192
@Nullable
9293
private SQLExceptionTranslator jdbcExceptionTranslator;
9394

95+
@Nullable
96+
private SQLExceptionTranslator transactionExceptionTranslator = new SQLExceptionSubclassTranslator();
97+
9498

9599
/**
96100
* Set whether to prepare the underlying JDBC Connection of a transactional
@@ -121,14 +125,22 @@ public void setPrepareConnection(boolean prepareConnection) {
121125
* <p>Applied to any detected {@link java.sql.SQLException} root cause of a Hibernate
122126
* {@link JDBCException}, overriding Hibernate's own {@code SQLException} translation
123127
* (which is based on a Hibernate Dialect for a specific target database).
128+
* <p>As of 6.1, also applied to {@link org.hibernate.TransactionException} translation
129+
* with a {@link SQLException} root cause (where Hibernate does not translate itself
130+
* at all), overriding Spring's default {@link SQLExceptionSubclassTranslator} there.
131+
* @param exceptionTranslator the {@link SQLExceptionTranslator} to delegate to, or
132+
* {@code null} for none. By default, a {@link SQLExceptionSubclassTranslator} will
133+
* be used for {@link org.hibernate.TransactionException} translation as of 6.1;
134+
* this can be reverted to pre-6.1 behavior through setting {@code null} here.
124135
* @since 5.1
125136
* @see java.sql.SQLException
126137
* @see org.hibernate.JDBCException
138+
* @see org.springframework.jdbc.support.SQLExceptionSubclassTranslator
127139
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
128-
* @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
129140
*/
130-
public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) {
131-
this.jdbcExceptionTranslator = jdbcExceptionTranslator;
141+
public void setJdbcExceptionTranslator(@Nullable SQLExceptionTranslator exceptionTranslator) {
142+
this.jdbcExceptionTranslator = exceptionTranslator;
143+
this.transactionExceptionTranslator = exceptionTranslator;
132144
}
133145

134146

@@ -245,7 +257,16 @@ protected DataAccessException convertHibernateAccessException(HibernateException
245257
DataAccessException dae = this.jdbcExceptionTranslator.translate(
246258
"Hibernate operation: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException());
247259
if (dae != null) {
248-
throw dae;
260+
return dae;
261+
}
262+
}
263+
if (this.transactionExceptionTranslator != null && ex instanceof org.hibernate.TransactionException) {
264+
if (ex.getCause() instanceof SQLException sqlEx) {
265+
DataAccessException dae = this.transactionExceptionTranslator.translate(
266+
"Hibernate transaction: " + ex.getMessage(), null, sqlEx);
267+
if (dae != null) {
268+
return dae;
269+
}
249270
}
250271
}
251272

spring-orm/src/main/java/org/springframework/orm/jpa/vendor/HibernateJpaVendorAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646
/**
4747
* {@link org.springframework.orm.jpa.JpaVendorAdapter} implementation for Hibernate.
48-
* Compatible with Hibernate ORM 5.5/5.6 as well as 6.0/6.1/6.2.
48+
* Compatible with Hibernate ORM 5.5/5.6 as well as 6.0/6.1/6.2/6.3.
4949
*
5050
* <p>Exposes Hibernate's persistence provider and Hibernate's Session as extended
5151
* EntityManager interface, and adapts {@link AbstractJpaVendorAdapter}'s common

0 commit comments

Comments
 (0)