diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index 1fafb7e75dd2..0b154146911e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -1133,13 +1133,18 @@ public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() { public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { return (sqlException, message, sql) -> switch ( extractErrorCode( sqlException ) ) { case -952 -> new LockTimeoutException( message, sqlException, sql ); - case -803 -> new ConstraintViolationException( - message, - sqlException, - sql, + case -803 -> new ConstraintViolationException( message, sqlException, sql, ConstraintViolationException.ConstraintKind.UNIQUE, - getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) - ); + getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); + case -530,-531 -> new ConstraintViolationException( message, sqlException, sql, + ConstraintViolationException.ConstraintKind.FOREIGN_KEY, + getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); + case -407 -> new ConstraintViolationException( message, sqlException, sql, + ConstraintViolationException.ConstraintKind.NOT_NULL, + getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); + case -543,-545 -> new ConstraintViolationException( message, sqlException, sql, + ConstraintViolationException.ConstraintKind.CHECK, + getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); default -> null; }; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index 9f43253ee251..1ebb46fcbce7 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -36,6 +36,7 @@ import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.exception.ConstraintViolationException; +import org.hibernate.exception.ConstraintViolationException.ConstraintKind; import org.hibernate.exception.LockAcquisitionException; import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor; @@ -807,23 +808,26 @@ public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { return (sqlException, message, sql) -> switch ( extractErrorCode( sqlException ) ) { case 23505 -> - // Unique constraint violation - new ConstraintViolationException( - message, - sqlException, - sql, - ConstraintViolationException.ConstraintKind.UNIQUE, - getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) - ); + // Unique index or primary key violation + new ConstraintViolationException( message, sqlException, sql, ConstraintKind.UNIQUE, + getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); case 40001 -> // DEADLOCK DETECTED new LockAcquisitionException(message, sqlException, sql); case 50200 -> // LOCK NOT AVAILABLE new PessimisticLockException(message, sqlException, sql); - case 90006 -> - // NULL not allowed for column [90006-145] - new ConstraintViolationException( message, sqlException, sql, + case 23502 -> + // NULL not allowed for column + new ConstraintViolationException( message, sqlException, sql, ConstraintKind.NOT_NULL, + getViolatedConstraintNameExtractor().extractConstraintName(sqlException) ); + case 23503, 23506 -> + // Referential integrity constraint violation + new ConstraintViolationException( message, sqlException, sql, ConstraintKind.FOREIGN_KEY, + getViolatedConstraintNameExtractor().extractConstraintName(sqlException) ); + case 23513, 23514 -> + // Check constraint violation + new ConstraintViolationException( message, sqlException, sql, ConstraintKind.CHECK, getViolatedConstraintNameExtractor().extractConstraintName(sqlException) ); case 57014 -> new QueryTimeoutException( message, sqlException, sql ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java index c0170a5cb985..42dfb6b57afd 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java @@ -349,13 +349,22 @@ public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { return new LockAcquisitionException( message, sqlException, sql ); case 1062: // Unique constraint violation - return new ConstraintViolationException( - message, - sqlException, - sql, + return new ConstraintViolationException( message, sqlException, sql, ConstraintViolationException.ConstraintKind.UNIQUE, getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); + case 1048: + // Null constraint violation + return new ConstraintViolationException( message, sqlException, sql, + ConstraintViolationException.ConstraintKind.NOT_NULL, null ); + case 1451, 1452: + // Foreign key constraint violation + return new ConstraintViolationException( message, sqlException, sql, + ConstraintViolationException.ConstraintKind.FOREIGN_KEY, null ); + case 3819: + // Check constraint violation + return new ConstraintViolationException( message, sqlException, sql, + ConstraintViolationException.ConstraintKind.CHECK, null ); } final String sqlState = extractSqlState( sqlException ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index 5f52eb6792b6..1253f7207a5b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -1253,13 +1253,21 @@ public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { return new LockAcquisitionException( message, sqlException, sql ); case 1062: // Unique constraint violation - return new ConstraintViolationException( - message, - sqlException, - sql, + return new ConstraintViolationException( message, sqlException, sql, ConstraintViolationException.ConstraintKind.UNIQUE, - getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) - ); + getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); + case 1048: + // Null constraint violation + return new ConstraintViolationException( message, sqlException, sql, + ConstraintViolationException.ConstraintKind.NOT_NULL, null ); + case 1451, 1452: + // Foreign key constraint violation + return new ConstraintViolationException( message, sqlException, sql, + ConstraintViolationException.ConstraintKind.FOREIGN_KEY, null ); + case 3819: + // Check constraint violation + return new ConstraintViolationException( message, sqlException, sql, + ConstraintViolationException.ConstraintKind.CHECK, null ); } final String sqlState = extractSqlState( sqlException ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index 9d5197cccf37..9a6a19594d97 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -39,6 +39,7 @@ import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.exception.ConstraintViolationException; +import org.hibernate.exception.ConstraintViolationException.ConstraintKind; import org.hibernate.exception.LockAcquisitionException; import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.spi.SQLExceptionConversionDelegate; @@ -1166,16 +1167,20 @@ public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { // data integrity violation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ case 1 -> // ORA-00001: unique constraint violated - new ConstraintViolationException( - message, - sqlException, - sql, - ConstraintViolationException.ConstraintKind.UNIQUE, - getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) - ); - case 1407 -> + new ConstraintViolationException( message, sqlException, sql, ConstraintKind.UNIQUE, + getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); + case 1400, 1407 -> + // ORA-01400: cannot insert NULL into column // ORA-01407: cannot update column to NULL - new ConstraintViolationException( message, sqlException, sql, + new ConstraintViolationException( message, sqlException, sql, ConstraintKind.NOT_NULL, + getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); + case 2291, 2292 -> + // ORA-02291, ORA-02292: integrity constraint violated + new ConstraintViolationException( message, sqlException, sql, ConstraintKind.FOREIGN_KEY, + getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); + case 2290 -> + //ORA-02290 check constraint violated + new ConstraintViolationException( message, sqlException, sql, ConstraintKind.CHECK, getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); default -> null; }; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java index 187c248f8e39..d349f43c3eee 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -830,13 +830,12 @@ public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { return switch ( extractErrorCode( sqlException ) ) { case 1222 -> new LockTimeoutException( message, sqlException, sql ); - case 2627, 2601 -> new ConstraintViolationException( - message, - sqlException, - sql, + case 2627, 2601 -> new ConstraintViolationException( message, sqlException, sql, ConstraintViolationException.ConstraintKind.UNIQUE, getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); + case 515 -> new ConstraintViolationException( message, sqlException, sql, + ConstraintViolationException.ConstraintKind.NOT_NULL, null ); default -> null; }; }; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java index 8eec40a1f65c..a7f61b2b6872 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java @@ -708,40 +708,27 @@ public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { switch ( errorCode ) { case 515: // Attempt to insert NULL value into column; column does not allow nulls. - return new ConstraintViolationException( - message, - sqlException, - sql, - getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) - ); + return new ConstraintViolationException( message, sqlException, sql, + ConstraintViolationException.ConstraintKind.NOT_NULL, + getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); case 546: // Foreign key violation - return new ConstraintViolationException( - message, - sqlException, - sql, - getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) - ); + return new ConstraintViolationException( message, sqlException, sql, + ConstraintViolationException.ConstraintKind.FOREIGN_KEY, + getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); case 2601: // Unique constraint violation - return new ConstraintViolationException( - message, - sqlException, - sql, + return new ConstraintViolationException( message, sqlException, sql, ConstraintViolationException.ConstraintKind.UNIQUE, - getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) - ); + getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); } break; case "ZZZZZ": if ( 515 == errorCode ) { // Attempt to insert NULL value into column; column does not allow nulls. - return new ConstraintViolationException( - message, - sqlException, - sql, - getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) - ); + return new ConstraintViolationException( message, sqlException, sql, + ConstraintViolationException.ConstraintKind.NOT_NULL, + getViolatedConstraintNameExtractor().extractConstraintName( sqlException ) ); } break; } diff --git a/hibernate-core/src/main/java/org/hibernate/exception/ConstraintViolationException.java b/hibernate-core/src/main/java/org/hibernate/exception/ConstraintViolationException.java index 181ace2b9e96..483122a43708 100644 --- a/hibernate-core/src/main/java/org/hibernate/exception/ConstraintViolationException.java +++ b/hibernate-core/src/main/java/org/hibernate/exception/ConstraintViolationException.java @@ -57,7 +57,10 @@ public ConstraintKind getKind() { } public enum ConstraintKind { + NOT_NULL, UNIQUE, + FOREIGN_KEY, + CHECK, OTHER } } diff --git a/hibernate-core/src/main/java/org/hibernate/exception/internal/SQLStateConversionDelegate.java b/hibernate-core/src/main/java/org/hibernate/exception/internal/SQLStateConversionDelegate.java index 4dc3ec04aec3..0b1b9d998ad3 100644 --- a/hibernate-core/src/main/java/org/hibernate/exception/internal/SQLStateConversionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/exception/internal/SQLStateConversionDelegate.java @@ -11,6 +11,7 @@ import org.hibernate.QueryTimeoutException; import org.hibernate.exception.AuthException; import org.hibernate.exception.ConstraintViolationException; +import org.hibernate.exception.ConstraintViolationException.ConstraintKind; import org.hibernate.exception.DataException; import org.hibernate.exception.JDBCConnectionException; import org.hibernate.exception.LockAcquisitionException; @@ -78,16 +79,22 @@ public SQLStateConversionDelegate(ConversionContext conversionContext) { "23", // "integrity constraint violation" "27", // "triggered data change violation" "44": // "with check option violation" - final String constraintName = getConversionContext() - .getViolatedConstraintNameExtractor() - .extractConstraintName( sqlException ); - return new ConstraintViolationException( message, sqlException, sql, constraintName ); + final String constraintName = + getConversionContext().getViolatedConstraintNameExtractor() + .extractConstraintName( sqlException ); + if ( sqlState.length() >= 5 ) { + final ConstraintKind constraintKind = constraintKind( sqlState.substring( 0, 5 ) ); + return new ConstraintViolationException( message, sqlException, sql, constraintKind, constraintName ); + } + else { + return new ConstraintViolationException( message, sqlException, sql, constraintName ); + } case "08": // "connection exception" return new JDBCConnectionException( message, sqlException, sql ); case "21", // "cardinality violation" - "22": // "data exception" + "22": // "data exception" (22001 is string too long) return new DataException( message, sqlException, sql ); case "28": // "authentication failure" @@ -96,4 +103,17 @@ public SQLStateConversionDelegate(ConversionContext conversionContext) { } return null; } + + private static ConstraintKind constraintKind(String trimmedState) { + return switch ( trimmedState ) { + case "23502" -> ConstraintKind.NOT_NULL; + case "23505" -> ConstraintKind.UNIQUE; + case "23503" -> ConstraintKind.FOREIGN_KEY; + // 23510-3 indicate CHECK on Db2, + // 23514 indicates CHECK on Postgres, + // 23513-4 indicate CHECK on h2 + case "23510", "23511", "23512", "23513", "23514" -> ConstraintKind.CHECK; + default -> ConstraintKind.OTHER; + }; + } }