diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacyDialect.java index 6f5d4e6e702e..f9ef9ec3ca50 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacyDialect.java @@ -216,6 +216,11 @@ public boolean supportsColumnCheck() { return getVersion().isSameOrAfter( 10, 2 ); } + @Override + public boolean supportsNamedColumnCheck() { + return false; + } + @Override public boolean doesRoundTemporalOnOverflow() { // See https://jira.mariadb.org/browse/MDEV-16991 diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java index f0385ea2ab6f..6bf98fddaf22 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java @@ -967,7 +967,7 @@ void applyCheckConstraints(jakarta.persistence.CheckConstraint[] checkConstraint if ( isNotEmpty( checkConstraintAnnotationUsages ) ) { for ( jakarta.persistence.CheckConstraint checkConstraintAnnotationUsage : checkConstraintAnnotationUsages ) { addCheckConstraint( - checkConstraintAnnotationUsage.name(), + nullIfEmpty( checkConstraintAnnotationUsage.name() ), checkConstraintAnnotationUsage.constraint(), checkConstraintAnnotationUsage.options() ); @@ -983,7 +983,7 @@ void applyCheckConstraint(PropertyData inferredData, int length) { if ( checksAnn != null ) { final Check[] checkAnns = checksAnn.value(); for ( Check checkAnn : checkAnns ) { - addCheckConstraint( checkAnn.name(), checkAnn.constraints() ); + addCheckConstraint( nullIfEmpty( checkAnn.name() ), checkAnn.constraints() ); } } else { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/TableBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/TableBinder.java index ed807176d6b2..8392cf29716a 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/TableBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/TableBinder.java @@ -44,6 +44,7 @@ import static org.hibernate.internal.util.StringHelper.isNotEmpty; import static org.hibernate.internal.util.StringHelper.isQuoted; import static org.hibernate.internal.util.StringHelper.nullIfBlank; +import static org.hibernate.internal.util.StringHelper.nullIfEmpty; import static org.hibernate.internal.util.StringHelper.unquote; import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty; @@ -896,7 +897,7 @@ static void addTableCheck( for ( jakarta.persistence.CheckConstraint checkConstraintAnnotationUsage : checkConstraintAnnotationUsages ) { table.addCheck( new CheckConstraint( - checkConstraintAnnotationUsage.name(), + nullIfEmpty( checkConstraintAnnotationUsage.name() ), checkConstraintAnnotationUsage.constraint(), checkConstraintAnnotationUsage.options() ) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index c6f14ad203d9..36a22063575a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -4186,6 +4186,16 @@ public boolean supportsColumnCheck() { return true; } + /** + * Does this dialect support named column-level check constraints? + * + * @return True if named column-level {@code check} constraints are supported; + * false otherwise. + */ + public boolean supportsNamedColumnCheck() { + return supportsColumnCheck(); + } + /** * Does this dialect support table-level check constraints? * 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 e2527fa8193e..cbe31addc501 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java @@ -245,6 +245,11 @@ public boolean supportsColumnCheck() { return true; } + @Override + public boolean supportsNamedColumnCheck() { + return false; + } + @Override public boolean doesRoundTemporalOnOverflow() { // See https://jira.mariadb.org/browse/MDEV-16991 diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java index 3130e3ed894f..c68bb8b359d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java @@ -144,34 +144,41 @@ private static void appendConstraints( } if ( dialect.supportsColumnCheck() ) { - // some databases (Maria, SQL Server) don't like multiple 'check' clauses final List checkConstraints = column.getCheckConstraints(); - long anonConstraints = checkConstraints.stream().filter(CheckConstraint::isAnonymous).count(); - if ( anonConstraints == 1 ) { - for ( CheckConstraint constraint : checkConstraints ) { - definition.append( constraint.constraintString( dialect ) ); + boolean hasAnonymousConstraints = false; + for ( CheckConstraint constraint : checkConstraints ) { + if ( constraint.isAnonymous() ) { + if ( !hasAnonymousConstraints ) { + definition.append(" check ("); + hasAnonymousConstraints = true; + } + else { + definition.append(" and "); + } + definition.append( constraint.getConstraintInParens() ); } } - else { - boolean first = true; + if ( hasAnonymousConstraints ) { + definition.append( ')' ); + } + + if ( !dialect.supportsTableCheck() ) { + // When table check constraints are not supported, try to render all named constraints for ( CheckConstraint constraint : checkConstraints ) { - if ( constraint.isAnonymous() ) { - if ( first ) { - definition.append(" check ("); - first = false; - } - else { - definition.append(" and "); - } - definition.append( constraint.getConstraintInParens() ); + if ( constraint.isNamed() ) { + definition.append( constraint.constraintString( dialect ) ); } } - if ( !first ) { - definition.append(")"); - } + } + else if ( !hasAnonymousConstraints && dialect.supportsNamedColumnCheck() ) { + // Otherwise only render the first named constraint as column constraint if there are no anonymous + // constraints and named column check constraint are supported, because some database don't like + // multiple check clauses. + // Note that the TableExporter will take care of named constraints then for ( CheckConstraint constraint : checkConstraints ) { if ( constraint.isNamed() ) { definition.append( constraint.constraintString( dialect ) ); + break; } } } diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/StandardTableExporter.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/StandardTableExporter.java index 98358f28aab0..f597d840371e 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/StandardTableExporter.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/StandardTableExporter.java @@ -197,37 +197,43 @@ protected void applyTableTypeString(StringBuilder buf) { protected void applyTableCheck(Table table, StringBuilder buf) { if ( dialect.supportsTableCheck() ) { - if ( !dialect.supportsColumnCheck() ) { - for ( Column column : table.getColumns() ) { - // some databases (Maria, SQL Server) don't like multiple 'check' clauses - final List checkConstraints = column.getCheckConstraints(); - long anonConstraints = checkConstraints.stream().filter( CheckConstraint::isAnonymous ).count(); - if ( anonConstraints == 1 ) { - for ( CheckConstraint constraint : checkConstraints ) { - buf.append( "," ).append( constraint.constraintString( dialect ) ); - } - } - else { - boolean first = true; - for ( CheckConstraint constraint : checkConstraints ) { - if ( constraint.isAnonymous() ) { - if ( first ) { - buf.append( "," ).append( " check (" ); - first = false; - } - else { - buf.append( " and " ); - } - buf.append( constraint.getConstraintInParens() ); + for ( Column column : table.getColumns() ) { + final List checkConstraints = column.getCheckConstraints(); + boolean hasAnonymousConstraints = false; + if ( !dialect.supportsColumnCheck() ) { + for ( CheckConstraint constraint : checkConstraints ) { + if ( constraint.isAnonymous() ) { + if ( !hasAnonymousConstraints ) { + buf.append( ", check (" ); + hasAnonymousConstraints = true; + } + else { + buf.append( " and " ); } + buf.append( constraint.getConstraintInParens() ); } - if ( !first ) { - buf.append( ")" ); + } + if ( hasAnonymousConstraints ) { + buf.append( ')' ); + } + } + else { + hasAnonymousConstraints = checkConstraints.stream().anyMatch( CheckConstraint::isAnonymous ); + } + + // Since some databases don't like when multiple check clauses appear for a colum definition, + // named constraints need to be hoisted to the table definition. + // Skip the first named constraint if the column has no anonymous constraints and the dialect + // supports named column check constraints, because ColumnDefinitions will render the first check + // constraint already. + boolean skipNextNamedConstraint = !hasAnonymousConstraints && dialect.supportsNamedColumnCheck(); + for ( CheckConstraint constraint : checkConstraints ) { + if ( constraint.isNamed() ) { + if ( skipNextNamedConstraint ) { + skipNextNamedConstraint = false; } - for ( CheckConstraint constraint : checkConstraints ) { - if ( constraint.isNamed() ) { - buf.append( constraint.constraintString( dialect ) ); - } + else { + buf.append( ',' ).append( constraint.constraintString( dialect ) ); } } }