Skip to content

HHH-19300 do a better job of interpreting constraint violations #9932

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
}
Expand Down
26 changes: 15 additions & 11 deletions hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ public ConstraintKind getKind() {
}

public enum ConstraintKind {
NOT_NULL,
UNIQUE,
FOREIGN_KEY,
CHECK,
OTHER
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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"
Expand All @@ -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;
};
}
}