HHH-18054 Support for JPA 3.2 @CheckConstraint
This commit is contained in:
parent
905e86a04d
commit
ff07248944
|
@ -41,6 +41,8 @@ import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||||
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||||
|
import org.hibernate.internal.util.StringHelper;
|
||||||
|
import org.hibernate.mapping.CheckConstraint;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||||
import org.hibernate.query.sqm.CastType;
|
import org.hibernate.query.sqm.CastType;
|
||||||
|
@ -1429,4 +1431,11 @@ public class MySQLLegacyDialect extends Dialect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String appendCheckConstraintOptions(CheckConstraint checkConstraint, String sqlCheckConstraint) {
|
||||||
|
if ( StringHelper.isNotEmpty( checkConstraint.getOptions() ) ) {
|
||||||
|
return sqlCheckConstraint + " " + checkConstraint.getOptions();
|
||||||
|
}
|
||||||
|
return sqlCheckConstraint;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
|
import org.hibernate.mapping.CheckConstraint;
|
||||||
import org.hibernate.mapping.UserDefinedType;
|
import org.hibernate.mapping.UserDefinedType;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||||
|
@ -1570,4 +1571,12 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String appendCheckConstraintOptions(CheckConstraint checkConstraint, String sqlCheckConstraint) {
|
||||||
|
if ( StringHelper.isNotEmpty( checkConstraint.getOptions() ) ) {
|
||||||
|
return sqlCheckConstraint + " " + checkConstraint.getOptions();
|
||||||
|
}
|
||||||
|
return sqlCheckConstraint;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,8 @@ import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||||
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||||
|
import org.hibernate.internal.util.StringHelper;
|
||||||
|
import org.hibernate.mapping.CheckConstraint;
|
||||||
import org.hibernate.mapping.Column;
|
import org.hibernate.mapping.Column;
|
||||||
import org.hibernate.query.sqm.CastType;
|
import org.hibernate.query.sqm.CastType;
|
||||||
import org.hibernate.query.sqm.FetchClauseType;
|
import org.hibernate.query.sqm.FetchClauseType;
|
||||||
|
@ -1159,4 +1161,21 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
||||||
public boolean supportsFromClauseInUpdate() {
|
public boolean supportsFromClauseInUpdate() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCheckConstraintString(CheckConstraint checkConstraint) {
|
||||||
|
final String constraintName = checkConstraint.getName();
|
||||||
|
return constraintName == null
|
||||||
|
?
|
||||||
|
" check " + getCheckConstraintOptions( checkConstraint ) + "(" + checkConstraint.getConstraint() + ")"
|
||||||
|
:
|
||||||
|
" constraint " + constraintName + " check " + getCheckConstraintOptions( checkConstraint ) + "(" + checkConstraint.getConstraint() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCheckConstraintOptions(CheckConstraint checkConstraint) {
|
||||||
|
if ( StringHelper.isNotEmpty( checkConstraint.getOptions() ) ) {
|
||||||
|
return checkConstraint.getOptions() + " ";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||||
import org.hibernate.boot.spi.PropertyData;
|
import org.hibernate.boot.spi.PropertyData;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
import org.hibernate.mapping.AggregateColumn;
|
import org.hibernate.mapping.AggregateColumn;
|
||||||
import org.hibernate.mapping.CheckConstraint;
|
import org.hibernate.mapping.CheckConstraint;
|
||||||
import org.hibernate.mapping.Column;
|
import org.hibernate.mapping.Column;
|
||||||
|
@ -213,6 +214,10 @@ public class AnnotatedColumn {
|
||||||
checkConstraints.add( new CheckConstraint( name, constraint ) );
|
checkConstraints.add( new CheckConstraint( name, constraint ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addCheckConstraint(String name, String constraint, String options) {
|
||||||
|
checkConstraints.add( new CheckConstraint( name, constraint, options ) );
|
||||||
|
}
|
||||||
|
|
||||||
// public String getComment() {
|
// public String getComment() {
|
||||||
// return comment;
|
// return comment;
|
||||||
// }
|
// }
|
||||||
|
@ -800,6 +805,7 @@ public class AnnotatedColumn {
|
||||||
annotatedColumn.setParent( parent );
|
annotatedColumn.setParent( parent );
|
||||||
annotatedColumn.applyColumnDefault( inferredData, numberOfColumns );
|
annotatedColumn.applyColumnDefault( inferredData, numberOfColumns );
|
||||||
annotatedColumn.applyGeneratedAs( inferredData, numberOfColumns );
|
annotatedColumn.applyGeneratedAs( inferredData, numberOfColumns );
|
||||||
|
annotatedColumn.applyColumnCheckConstraint( column );
|
||||||
annotatedColumn.applyCheckConstraint( inferredData, numberOfColumns );
|
annotatedColumn.applyCheckConstraint( inferredData, numberOfColumns );
|
||||||
annotatedColumn.extractDataFromPropertyData( propertyHolder, inferredData );
|
annotatedColumn.extractDataFromPropertyData( propertyHolder, inferredData );
|
||||||
annotatedColumn.bind();
|
annotatedColumn.bind();
|
||||||
|
@ -873,7 +879,23 @@ public class AnnotatedColumn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyCheckConstraint(PropertyData inferredData, int length) {
|
private void applyColumnCheckConstraint(AnnotationUsage<jakarta.persistence.Column> column) {
|
||||||
|
applyCheckConstraints( column.findAttributeValue( "check" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyCheckConstraints(List<AnnotationUsage<jakarta.persistence.CheckConstraint>> checkConstraintAnnotationUsages) {
|
||||||
|
if ( CollectionHelper.isNotEmpty( checkConstraintAnnotationUsages ) ) {
|
||||||
|
for ( AnnotationUsage<jakarta.persistence.CheckConstraint> checkConstraintAnnotationUsage : checkConstraintAnnotationUsages ) {
|
||||||
|
addCheckConstraint(
|
||||||
|
checkConstraintAnnotationUsage.getString( "name" ),
|
||||||
|
checkConstraintAnnotationUsage.getString( "constraint" ),
|
||||||
|
checkConstraintAnnotationUsage.getString( "options" )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyCheckConstraint(PropertyData inferredData, int length) {
|
||||||
final MemberDetails attributeMember = inferredData.getAttributeMember();
|
final MemberDetails attributeMember = inferredData.getAttributeMember();
|
||||||
if ( attributeMember != null ) {
|
if ( attributeMember != null ) {
|
||||||
// if there are multiple annotations, they're not overrideable
|
// if there are multiple annotations, they're not overrideable
|
||||||
|
|
|
@ -195,6 +195,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
|
||||||
setInsertable( joinColumn.getBoolean( "insertable" ) );
|
setInsertable( joinColumn.getBoolean( "insertable" ) );
|
||||||
setUpdatable( joinColumn.getBoolean( "updatable" ) );
|
setUpdatable( joinColumn.getBoolean( "updatable" ) );
|
||||||
setReferencedColumn( joinColumn.getString( "referencedColumnName" ) );
|
setReferencedColumn( joinColumn.getString( "referencedColumnName" ) );
|
||||||
|
applyColumnCheckConstraint( joinColumn );
|
||||||
|
|
||||||
final String table = joinColumn.getString( "table" );
|
final String table = joinColumn.getString( "table" );
|
||||||
if ( table.isEmpty() ) {
|
if ( table.isEmpty() ) {
|
||||||
|
@ -516,4 +517,8 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
|
||||||
public void setParent(AnnotatedJoinColumns parent) {
|
public void setParent(AnnotatedJoinColumns parent) {
|
||||||
super.setParent( parent );
|
super.setParent( parent );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyColumnCheckConstraint(AnnotationUsage<jakarta.persistence.JoinColumn> column) {
|
||||||
|
applyCheckConstraints( column.findAttributeValue( "check" ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2584,6 +2584,11 @@ public abstract class CollectionBinder {
|
||||||
property.forEachAnnotationUsage( Check.class, (usage) -> {
|
property.forEachAnnotationUsage( Check.class, (usage) -> {
|
||||||
addCheckToCollection( collectionTable, usage );
|
addCheckToCollection( collectionTable, usage );
|
||||||
} );
|
} );
|
||||||
|
property.forEachAnnotationUsage(
|
||||||
|
jakarta.persistence.JoinTable.class,
|
||||||
|
(usage) ->
|
||||||
|
TableBinder.addTableCheck( collectionTable, usage.findAttributeValue( "check" ) )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addCheckToCollection(Table collectionTable, AnnotationUsage<Check> check) {
|
private static void addCheckToCollection(Table collectionTable, AnnotationUsage<Check> check) {
|
||||||
|
|
|
@ -447,7 +447,9 @@ public class EntityBinder {
|
||||||
|
|
||||||
final AnnotationUsage<jakarta.persistence.Table> jpaTableUsage = annotatedClass.getAnnotationUsage( jakarta.persistence.Table.class );
|
final AnnotationUsage<jakarta.persistence.Table> jpaTableUsage = annotatedClass.getAnnotationUsage( jakarta.persistence.Table.class );
|
||||||
if ( jpaTableUsage != null ) {
|
if ( jpaTableUsage != null ) {
|
||||||
TableBinder.addJpaIndexes( persistentClass.getTable(), jpaTableUsage.getList( "indexes" ), context );
|
final Table table = persistentClass.getTable();
|
||||||
|
TableBinder.addJpaIndexes( table, jpaTableUsage.getList( "indexes" ), context );
|
||||||
|
TableBinder.addTableCheck( table, jpaTableUsage.findAttributeValue( "check" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
final InFlightMetadataCollector.EntityTableXref entityTableXref = context
|
final InFlightMetadataCollector.EntityTableXref entityTableXref = context
|
||||||
|
@ -2167,7 +2169,7 @@ public class EntityBinder {
|
||||||
|
|
||||||
//Used for @*ToMany @JoinTable
|
//Used for @*ToMany @JoinTable
|
||||||
public Join addJoinTable(AnnotationUsage<JoinTable> joinTable, PropertyHolder holder, boolean noDelayInPkColumnCreation) {
|
public Join addJoinTable(AnnotationUsage<JoinTable> joinTable, PropertyHolder holder, boolean noDelayInPkColumnCreation) {
|
||||||
return addJoin(
|
final Join join = addJoin(
|
||||||
holder,
|
holder,
|
||||||
noDelayInPkColumnCreation,
|
noDelayInPkColumnCreation,
|
||||||
false,
|
false,
|
||||||
|
@ -2177,6 +2179,8 @@ public class EntityBinder {
|
||||||
joinTable.getList( "joinColumns" ),
|
joinTable.getList( "joinColumns" ),
|
||||||
joinTable.getList( "uniqueConstraints" )
|
joinTable.getList( "uniqueConstraints" )
|
||||||
);
|
);
|
||||||
|
TableBinder.addTableCheck( join.getTable(), joinTable.findAttributeValue( "check" ));
|
||||||
|
return join;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Join addSecondaryTable(AnnotationUsage<SecondaryTable> secondaryTable, PropertyHolder holder, boolean noDelayInPkColumnCreation) {
|
public Join addSecondaryTable(AnnotationUsage<SecondaryTable> secondaryTable, PropertyHolder holder, boolean noDelayInPkColumnCreation) {
|
||||||
|
@ -2192,6 +2196,7 @@ public class EntityBinder {
|
||||||
);
|
);
|
||||||
final Table table = join.getTable();
|
final Table table = join.getTable();
|
||||||
new IndexBinder( context ).bindIndexes( table, secondaryTable.getList( "indexes" ) );
|
new IndexBinder( context ).bindIndexes( table, secondaryTable.getList( "indexes" ) );
|
||||||
|
TableBinder.addTableCheck( table, secondaryTable.findAttributeValue( "check" ));
|
||||||
return join;
|
return join;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,8 @@ import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.mapping.Any;
|
import org.hibernate.mapping.Any;
|
||||||
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
|
import org.hibernate.mapping.CheckConstraint;
|
||||||
import org.hibernate.mapping.Collection;
|
import org.hibernate.mapping.Collection;
|
||||||
import org.hibernate.mapping.Column;
|
import org.hibernate.mapping.Column;
|
||||||
import org.hibernate.mapping.Component;
|
import org.hibernate.mapping.Component;
|
||||||
|
@ -870,6 +872,22 @@ public class TableBinder {
|
||||||
new IndexBinder( context ).bindIndexes( table, indexes );
|
new IndexBinder( context ).bindIndexes( table, indexes );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void addTableCheck(
|
||||||
|
Table table,
|
||||||
|
List<AnnotationUsage<jakarta.persistence.CheckConstraint>> checkConstraintAnnotationUsages) {
|
||||||
|
if ( CollectionHelper.isNotEmpty( checkConstraintAnnotationUsages ) ) {
|
||||||
|
for ( AnnotationUsage<jakarta.persistence.CheckConstraint> checkConstraintAnnotationUsage : checkConstraintAnnotationUsages ) {
|
||||||
|
table.addCheck(
|
||||||
|
new CheckConstraint(
|
||||||
|
checkConstraintAnnotationUsage.getString( "name" ),
|
||||||
|
checkConstraintAnnotationUsage.getString( "constraint" ),
|
||||||
|
checkConstraintAnnotationUsage.getString( "options" )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setDefaultName(
|
public void setDefaultName(
|
||||||
String ownerClassName,
|
String ownerClassName,
|
||||||
String ownerEntity,
|
String ownerEntity,
|
||||||
|
|
|
@ -109,6 +109,7 @@ import org.hibernate.internal.util.MathHelper;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||||
import org.hibernate.loader.ast.spi.MultiKeyLoadSizingStrategy;
|
import org.hibernate.loader.ast.spi.MultiKeyLoadSizingStrategy;
|
||||||
|
import org.hibernate.mapping.CheckConstraint;
|
||||||
import org.hibernate.mapping.Column;
|
import org.hibernate.mapping.Column;
|
||||||
import org.hibernate.mapping.Constraint;
|
import org.hibernate.mapping.Constraint;
|
||||||
import org.hibernate.mapping.ForeignKey;
|
import org.hibernate.mapping.ForeignKey;
|
||||||
|
@ -5627,4 +5628,30 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
||||||
return FunctionalDependencyAnalysisSupportImpl.NONE;
|
return FunctionalDependencyAnalysisSupportImpl.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a SQL check condition for {@link CheckConstraint}
|
||||||
|
*
|
||||||
|
* @return a SQL expression representing the {@link CheckConstraint}
|
||||||
|
*/
|
||||||
|
public String getCheckConstraintString(CheckConstraint checkConstraint) {
|
||||||
|
final String constraintName = checkConstraint.getName();
|
||||||
|
String constraint = constraintName == null
|
||||||
|
? " check (" + checkConstraint.getConstraint() + ")"
|
||||||
|
: " constraint " + constraintName + " check (" + checkConstraint.getConstraint() + ")";
|
||||||
|
return appendCheckConstraintOptions( checkConstraint, constraint );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append the {@link CheckConstraint} options to SQL check sqlCheckConstraint
|
||||||
|
*
|
||||||
|
* @param checkConstraint an instance of {@link CheckConstraint}
|
||||||
|
* @param sqlCheckConstraint the SQL to append the {@link CheckConstraint} options
|
||||||
|
*
|
||||||
|
* @return a SQL expression
|
||||||
|
*/
|
||||||
|
public String appendCheckConstraintOptions(CheckConstraint checkConstraint, String sqlCheckConstraint) {
|
||||||
|
return sqlCheckConstraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,8 @@ import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||||
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||||
|
import org.hibernate.internal.util.StringHelper;
|
||||||
|
import org.hibernate.mapping.CheckConstraint;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||||
import org.hibernate.query.sqm.CastType;
|
import org.hibernate.query.sqm.CastType;
|
||||||
|
@ -1571,4 +1573,12 @@ public class MySQLDialect extends Dialect {
|
||||||
public boolean supportsFromClauseInUpdate() {
|
public boolean supportsFromClauseInUpdate() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String appendCheckConstraintOptions(CheckConstraint checkConstraint, String sqlCheckConstraint) {
|
||||||
|
if ( StringHelper.isNotEmpty( checkConstraint.getOptions() ) ) {
|
||||||
|
return sqlCheckConstraint + " " + checkConstraint.getOptions();
|
||||||
|
}
|
||||||
|
return sqlCheckConstraint;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,8 +49,10 @@ import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||||
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||||
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||||
import org.hibernate.mapping.UserDefinedType;
|
import org.hibernate.mapping.UserDefinedType;
|
||||||
|
import org.hibernate.mapping.CheckConstraint;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||||
|
@ -1692,4 +1694,12 @@ public class OracleDialect extends Dialect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String appendCheckConstraintOptions(CheckConstraint checkConstraint, String sqlCheckConstraint) {
|
||||||
|
if ( StringHelper.isNotEmpty( checkConstraint.getOptions() ) ) {
|
||||||
|
return sqlCheckConstraint + " " + checkConstraint.getOptions();
|
||||||
|
}
|
||||||
|
return sqlCheckConstraint;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,8 @@ import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
||||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||||
|
import org.hibernate.internal.util.StringHelper;
|
||||||
|
import org.hibernate.mapping.CheckConstraint;
|
||||||
import org.hibernate.mapping.Column;
|
import org.hibernate.mapping.Column;
|
||||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||||
import org.hibernate.procedure.internal.SQLServerCallableStatementSupport;
|
import org.hibernate.procedure.internal.SQLServerCallableStatementSupport;
|
||||||
|
@ -1193,4 +1195,20 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
||||||
return SQLServerCallableStatementSupport.INSTANCE;
|
return SQLServerCallableStatementSupport.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCheckConstraintString(CheckConstraint checkConstraint) {
|
||||||
|
final String constraintName = checkConstraint.getName();
|
||||||
|
return constraintName == null
|
||||||
|
?
|
||||||
|
" check " + getCheckConstraintOptions( checkConstraint ) + "(" + checkConstraint.getConstraint() + ")"
|
||||||
|
:
|
||||||
|
" constraint " + constraintName + " check " + getCheckConstraintOptions( checkConstraint ) + "(" + checkConstraint.getConstraint() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCheckConstraintOptions(CheckConstraint checkConstraint) {
|
||||||
|
if ( StringHelper.isNotEmpty( checkConstraint.getOptions() ) ) {
|
||||||
|
return checkConstraint.getOptions() + " ";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ package org.hibernate.mapping;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.Dialect;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a table or column level {@code check} constraint.
|
* Represents a table or column level {@code check} constraint.
|
||||||
*
|
*
|
||||||
|
@ -16,12 +18,19 @@ import java.util.Objects;
|
||||||
public class CheckConstraint {
|
public class CheckConstraint {
|
||||||
private String name;
|
private String name;
|
||||||
private String constraint;
|
private String constraint;
|
||||||
|
private String options;
|
||||||
|
|
||||||
public CheckConstraint(String name, String constraint) {
|
public CheckConstraint(String name, String constraint) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.constraint = constraint;
|
this.constraint = constraint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CheckConstraint(String name, String constraint, String options) {
|
||||||
|
this.name = name;
|
||||||
|
this.constraint = constraint;
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
public CheckConstraint(String constraint) {
|
public CheckConstraint(String constraint) {
|
||||||
this.constraint = constraint;
|
this.constraint = constraint;
|
||||||
}
|
}
|
||||||
|
@ -54,12 +63,28 @@ public class CheckConstraint {
|
||||||
this.constraint = constraint;
|
this.constraint = constraint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getOptions() {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOptions(String options) {
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #constraintString(Dialect)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "7.0")
|
||||||
public String constraintString() {
|
public String constraintString() {
|
||||||
return name == null
|
return name == null
|
||||||
? " check (" + constraint + ")"
|
? " check (" + constraint + ")"
|
||||||
: " constraint " + name + " check (" + constraint + ")";
|
: " constraint " + name + " check (" + constraint + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String constraintString(Dialect dialect) {
|
||||||
|
return dialect.getCheckConstraintString( this );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object object) {
|
public boolean equals(Object object) {
|
||||||
if ( object instanceof CheckConstraint ) {
|
if ( object instanceof CheckConstraint ) {
|
||||||
|
|
|
@ -137,7 +137,7 @@ class ColumnDefinitions {
|
||||||
long anonConstraints = checkConstraints.stream().filter(CheckConstraint::isAnonymous).count();
|
long anonConstraints = checkConstraints.stream().filter(CheckConstraint::isAnonymous).count();
|
||||||
if ( anonConstraints == 1 ) {
|
if ( anonConstraints == 1 ) {
|
||||||
for ( CheckConstraint constraint : checkConstraints ) {
|
for ( CheckConstraint constraint : checkConstraints ) {
|
||||||
definition.append( constraint.constraintString() );
|
definition.append( constraint.constraintString( dialect ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -159,7 +159,7 @@ class ColumnDefinitions {
|
||||||
}
|
}
|
||||||
for ( CheckConstraint constraint : checkConstraints ) {
|
for ( CheckConstraint constraint : checkConstraints ) {
|
||||||
if ( constraint.isNamed() ) {
|
if ( constraint.isNamed() ) {
|
||||||
definition.append( constraint.constraintString() );
|
definition.append( constraint.constraintString( dialect ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,7 @@ public class StandardTableExporter implements Exporter<Table> {
|
||||||
protected void applyTableCheck(Table table, StringBuilder buf) {
|
protected void applyTableCheck(Table table, StringBuilder buf) {
|
||||||
if ( dialect.supportsTableCheck() ) {
|
if ( dialect.supportsTableCheck() ) {
|
||||||
for ( CheckConstraint constraint : table.getChecks() ) {
|
for ( CheckConstraint constraint : table.getChecks() ) {
|
||||||
buf.append( "," ).append( constraint.constraintString() );
|
buf.append( "," ).append( constraint.constraintString( dialect ) );
|
||||||
}
|
}
|
||||||
final AggregateSupport aggregateSupport = dialect.getAggregateSupport();
|
final AggregateSupport aggregateSupport = dialect.getAggregateSupport();
|
||||||
if ( aggregateSupport != null && aggregateSupport.supportsComponentCheckConstraints() ) {
|
if ( aggregateSupport != null && aggregateSupport.supportsComponentCheckConstraints() ) {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
package org.hibernate.orm.test.schemaupdate.checkconstraint.column;
|
||||||
|
|
||||||
|
public interface Another {
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.hibernate.orm.test.schemaupdate.checkconstraint.column;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
@Entity(name = "AnotherTestEntity")
|
||||||
|
@Table(name = "ANOTHER_TEST_ENTITY")
|
||||||
|
public class AnotherTestEntity implements Another {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "FIRST_NAME")
|
||||||
|
private String firstName;
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
package org.hibernate.orm.test.schemaupdate.checkconstraint.column;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.hibernate.boot.MetadataSources;
|
||||||
|
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||||
|
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||||
|
import org.hibernate.boot.spi.MetadataImplementor;
|
||||||
|
import org.hibernate.tool.hbm2ddl.SchemaExport;
|
||||||
|
import org.hibernate.tool.schema.TargetType;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.BaseUnitTest;
|
||||||
|
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
|
||||||
|
import org.hibernate.testing.orm.junit.JiraKey;
|
||||||
|
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
|
||||||
|
import org.hibernate.testing.util.ServiceRegistryUtil;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@BaseUnitTest
|
||||||
|
@JiraKey("HHH-18054")
|
||||||
|
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsColumnCheck.class)
|
||||||
|
public class ColumnCheckConstraintTest {
|
||||||
|
static final String COLUMN_CONSTRAINTS = "name_column <> null";
|
||||||
|
|
||||||
|
static final String ONE_TO_ONE_JOIN_COLUMN_CONSTRAINTS = "ID <> null";
|
||||||
|
static final String ONE_TO_MANY_JOIN_COLUMN_CONSTRAINTS = "ID > 2";
|
||||||
|
static final String MANY_TO_ONE_JOIN_COLUMN_CONSTRAINTS = "ID > 3 ";
|
||||||
|
static final String MANY_TO_MANY_JOIN_COLUMN_CONSTRAINTS = "ID > 4";
|
||||||
|
static final String MANY_TO_MANY_INVERSE_JOIN_COLUMN_CONSTRAINTS = "ID > 5";
|
||||||
|
static final String ANY_JOIN_COLUMN_CONSTRAINTS = "ID > 5";
|
||||||
|
|
||||||
|
static final String ONE_TO_ONE_JOIN_COLUMN_NAME = "ONE_TO_ONE_JOIN_COLUMN_NAME";
|
||||||
|
static final String ONE_TO_MANY_JOIN_COLUMN_NAME = "ONE_TO_MAIN_JOIN_COLUMN_NAME";
|
||||||
|
static final String MANY_TO_ONE_JOIN_COLUMN_NAME = "MANY_TO_ONE_JOIN_COLUMN_NAME";
|
||||||
|
static final String MANY_TO_MANY_JOIN_COLUMN_NAME = "MANY_TO_MANY_JOIN_COLUMN_NAME";
|
||||||
|
static final String MANY_TO_MANY_INVERSE_JOIN_COLUMN_NAME = "MANY_TO_MANY_INVERSE_JOIN_COLUMN_NAME";
|
||||||
|
|
||||||
|
private File output;
|
||||||
|
private StandardServiceRegistry ssr;
|
||||||
|
private MetadataImplementor metadata;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() throws IOException {
|
||||||
|
output = File.createTempFile( "update_script", ".sql" );
|
||||||
|
output.deleteOnExit();
|
||||||
|
ssr = ServiceRegistryUtil.serviceRegistry();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void tearsDown() {
|
||||||
|
output.delete();
|
||||||
|
StandardServiceRegistryBuilder.destroy( ssr );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testColumnConstraintsAreApplied() throws Exception {
|
||||||
|
createSchema( TestEntity.class, AnotherTestEntity.class );
|
||||||
|
String fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase()
|
||||||
|
.replace( System.lineSeparator(), "" );
|
||||||
|
assertThat( fileContent.toUpperCase( Locale.ROOT ) ).contains( COLUMN_CONSTRAINTS.toUpperCase( Locale.ROOT ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJoinColumConstraintsAreApplied() throws Exception {
|
||||||
|
createSchema( TestEntity.class, AnotherTestEntity.class );
|
||||||
|
String[] fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase()
|
||||||
|
.split( System.lineSeparator() );
|
||||||
|
assertTrue( tableCreationStatementContainsConstraints(
|
||||||
|
fileContent,
|
||||||
|
"TEST_ENTITY",
|
||||||
|
MANY_TO_ONE_JOIN_COLUMN_CONSTRAINTS
|
||||||
|
), "Check Constraints on ManyToOne join table have not been created" );
|
||||||
|
assertTrue( tableCreationStatementContainsConstraints(
|
||||||
|
fileContent,
|
||||||
|
"TEST_ENTITY",
|
||||||
|
ONE_TO_ONE_JOIN_COLUMN_CONSTRAINTS
|
||||||
|
), "Check Constraints on OneToOne join table have not been created" );
|
||||||
|
assertTrue( tableCreationStatementContainsConstraints(
|
||||||
|
fileContent,
|
||||||
|
"ANOTHER_TEST_ENTITY",
|
||||||
|
ONE_TO_MANY_JOIN_COLUMN_CONSTRAINTS
|
||||||
|
), "Check Constraints on OneToOne join table have not been created" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJoinColumOfJoinTableConstraintsAreApplied() throws Exception {
|
||||||
|
createSchema( TestEntity.class, AnotherTestEntity.class );
|
||||||
|
String[] fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase()
|
||||||
|
.split( System.lineSeparator() );
|
||||||
|
assertTrue( tableCreationStatementContainsConstraints(
|
||||||
|
fileContent,
|
||||||
|
"MANY_T0_MANY_TABLE",
|
||||||
|
MANY_TO_MANY_JOIN_COLUMN_CONSTRAINTS
|
||||||
|
), "Join column Check Constraints on ManyToMany join table have not been created" );
|
||||||
|
assertTrue( tableCreationStatementContainsConstraints(
|
||||||
|
fileContent,
|
||||||
|
"MANY_T0_MANY_TABLE",
|
||||||
|
MANY_TO_MANY_INVERSE_JOIN_COLUMN_CONSTRAINTS
|
||||||
|
), "Inverse join column Check Constraints on ManyToMany join table have not been created" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAnyJoinTableConstraintsAreApplied() throws Exception {
|
||||||
|
createSchema( TestEntity.class, AnotherTestEntity.class );
|
||||||
|
String[] fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase()
|
||||||
|
.split( System.lineSeparator() );
|
||||||
|
assertTrue( tableCreationStatementContainsConstraints(
|
||||||
|
fileContent,
|
||||||
|
"TEST_ENTITY",
|
||||||
|
ANY_JOIN_COLUMN_CONSTRAINTS
|
||||||
|
), "Check Constraints on Any join table have not been created" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean tableCreationStatementContainsConstraints(
|
||||||
|
String[] fileContent,
|
||||||
|
String tableName,
|
||||||
|
String secondaryTableConstraints) {
|
||||||
|
for ( int i = 0; i < fileContent.length; i++ ) {
|
||||||
|
String statement = fileContent[i].toUpperCase( Locale.ROOT );
|
||||||
|
if ( statement.contains( "CREATE TABLE " + tableName.toUpperCase( Locale.ROOT ) ) ) {
|
||||||
|
if ( statement.contains( secondaryTableConstraints.toUpperCase( Locale.ROOT ) ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createSchema(Class... annotatedClasses) {
|
||||||
|
final MetadataSources metadataSources = new MetadataSources( ssr );
|
||||||
|
|
||||||
|
for ( Class c : annotatedClasses ) {
|
||||||
|
metadataSources.addAnnotatedClass( c );
|
||||||
|
}
|
||||||
|
metadata = (MetadataImplementor) metadataSources.buildMetadata();
|
||||||
|
metadata.orderColumns( false );
|
||||||
|
metadata.validate();
|
||||||
|
new SchemaExport()
|
||||||
|
.setHaltOnError( true )
|
||||||
|
.setOutputFile( output.getAbsolutePath() )
|
||||||
|
.setFormat( false )
|
||||||
|
.create( EnumSet.of( TargetType.SCRIPT ), metadata );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
package org.hibernate.orm.test.schemaupdate.checkconstraint.column;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Any;
|
||||||
|
import org.hibernate.annotations.AnyDiscriminator;
|
||||||
|
import org.hibernate.annotations.AnyDiscriminatorValue;
|
||||||
|
import org.hibernate.annotations.AnyDiscriminatorValues;
|
||||||
|
import org.hibernate.annotations.AnyKeyJavaClass;
|
||||||
|
|
||||||
|
import jakarta.persistence.CheckConstraint;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.DiscriminatorType;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.JoinTable;
|
||||||
|
import jakarta.persistence.ManyToMany;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.OneToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "TEST_ENTITY")
|
||||||
|
public class TestEntity {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(
|
||||||
|
name = "name_column",
|
||||||
|
check = {
|
||||||
|
@CheckConstraint(
|
||||||
|
name = "column_constraint",
|
||||||
|
constraint = ColumnCheckConstraintTest.COLUMN_CONSTRAINTS,
|
||||||
|
options = "enforced"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@OneToOne
|
||||||
|
@JoinColumn(
|
||||||
|
name = ColumnCheckConstraintTest.ONE_TO_ONE_JOIN_COLUMN_NAME,
|
||||||
|
check = {
|
||||||
|
@CheckConstraint(
|
||||||
|
name = "ONE_TO_ONE_JOIN_COLUMN_CONSTRAINT",
|
||||||
|
constraint = ColumnCheckConstraintTest.ONE_TO_ONE_JOIN_COLUMN_CONSTRAINTS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
private AnotherTestEntity entity;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(
|
||||||
|
name = ColumnCheckConstraintTest.MANY_TO_ONE_JOIN_COLUMN_NAME,
|
||||||
|
check = {
|
||||||
|
@CheckConstraint(
|
||||||
|
name = "MANY_TO_ONE_JOIN_COLUMN_CONSTRAINT",
|
||||||
|
constraint = ColumnCheckConstraintTest.MANY_TO_ONE_JOIN_COLUMN_CONSTRAINTS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
private AnotherTestEntity testEntity;
|
||||||
|
|
||||||
|
@OneToMany
|
||||||
|
@JoinColumn(
|
||||||
|
name = ColumnCheckConstraintTest.ONE_TO_MANY_JOIN_COLUMN_NAME,
|
||||||
|
check = {
|
||||||
|
@CheckConstraint(
|
||||||
|
name = "ONE_TO_MANY_JOIN_COLUMN_CONSTRAINT",
|
||||||
|
constraint = ColumnCheckConstraintTest.ONE_TO_MANY_JOIN_COLUMN_CONSTRAINTS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
private List<AnotherTestEntity> testEntities;
|
||||||
|
|
||||||
|
@ManyToMany
|
||||||
|
@JoinTable(
|
||||||
|
name = "MANY_T0_MANY_TABLE",
|
||||||
|
inverseJoinColumns = {
|
||||||
|
@JoinColumn(
|
||||||
|
name = ColumnCheckConstraintTest.MANY_TO_MANY_INVERSE_JOIN_COLUMN_NAME,
|
||||||
|
check = {
|
||||||
|
@CheckConstraint(
|
||||||
|
name = "MANY_TO_MANY_INVERSE_JOIN_COLUMN_CONSTRAINT",
|
||||||
|
constraint = ColumnCheckConstraintTest.MANY_TO_MANY_INVERSE_JOIN_COLUMN_CONSTRAINTS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
},
|
||||||
|
joinColumns = {
|
||||||
|
@JoinColumn(
|
||||||
|
name = ColumnCheckConstraintTest.MANY_TO_MANY_JOIN_COLUMN_NAME,
|
||||||
|
check = {
|
||||||
|
@CheckConstraint(
|
||||||
|
name = "MANY_TO_MANY_JOIN_COLUMN_CONSTRAINT",
|
||||||
|
constraint = ColumnCheckConstraintTest.MANY_TO_MANY_JOIN_COLUMN_CONSTRAINTS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
private List<AnotherTestEntity> testEntities2;
|
||||||
|
|
||||||
|
@Any
|
||||||
|
@AnyDiscriminator(DiscriminatorType.STRING)
|
||||||
|
@AnyDiscriminatorValues({
|
||||||
|
@AnyDiscriminatorValue(discriminator = "S", entity = AnotherTestEntity.class),
|
||||||
|
})
|
||||||
|
@AnyKeyJavaClass(Long.class)
|
||||||
|
@Column(name = "another_type")
|
||||||
|
@JoinColumn(name = "another_id",
|
||||||
|
check = {
|
||||||
|
@CheckConstraint(
|
||||||
|
name = "ANY_JOIN_COLUMN_CONSTRAINT",
|
||||||
|
constraint = ColumnCheckConstraintTest.ANY_JOIN_COLUMN_CONSTRAINTS
|
||||||
|
)
|
||||||
|
})
|
||||||
|
private Another another;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
package org.hibernate.orm.test.schemaupdate.checkconstraint.table;
|
||||||
|
|
||||||
|
public interface Another {
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.hibernate.orm.test.schemaupdate.checkconstraint.table;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
|
||||||
|
@Entity(name = "AnotherTestEntity")
|
||||||
|
public class AnotherTestEntity implements Another {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "FIRST_NAME")
|
||||||
|
private String firstName;
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
package org.hibernate.orm.test.schemaupdate.checkconstraint.table;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Any;
|
||||||
|
import org.hibernate.annotations.AnyDiscriminator;
|
||||||
|
import org.hibernate.annotations.AnyDiscriminatorValue;
|
||||||
|
import org.hibernate.annotations.AnyDiscriminatorValues;
|
||||||
|
import org.hibernate.annotations.AnyKeyJavaClass;
|
||||||
|
|
||||||
|
import jakarta.persistence.CheckConstraint;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.DiscriminatorType;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.JoinTable;
|
||||||
|
import jakarta.persistence.ManyToMany;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.OneToOne;
|
||||||
|
import jakarta.persistence.SecondaryTable;
|
||||||
|
|
||||||
|
@Entity(name = "TEST_ENTITY_2")
|
||||||
|
@SecondaryTable(
|
||||||
|
name = TableCheckConstraintTest.SECONDARY_TABLE_NAME,
|
||||||
|
check = {
|
||||||
|
@CheckConstraint(
|
||||||
|
name = "TABLE_CONSTRAINT",
|
||||||
|
constraint = TableCheckConstraintTest.SECONDARY_TABLE_CONSTRAINTS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public class EntityWithSecondaryTables {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "NAME_COLUMN")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(name = "SECOND_NAME", table = TableCheckConstraintTest.SECONDARY_TABLE_NAME)
|
||||||
|
private String secondName;
|
||||||
|
|
||||||
|
@OneToOne
|
||||||
|
@JoinTable(
|
||||||
|
name = TableCheckConstraintTest.ONE_TO_ONE_JOIN_TABLE_NAME,
|
||||||
|
check = {
|
||||||
|
@CheckConstraint(
|
||||||
|
name = "ONE_TO_ONE_JOIN_TABLE_CONSTRAINT",
|
||||||
|
constraint = TableCheckConstraintTest.ONE_TO_ONE_JOIN_TABLE_CONSTRAINTS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
private AnotherTestEntity entity;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinTable(
|
||||||
|
name = TableCheckConstraintTest.MANY_TO_ONE_JOIN_TABLE_NAME,
|
||||||
|
check = {
|
||||||
|
@CheckConstraint(
|
||||||
|
name = "MANY_TO_ONE_JOIN_TABLE_CONSTRAINT",
|
||||||
|
constraint = TableCheckConstraintTest.MANY_TO_ONE_JOIN_TABLE_CONSTRAINTS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
private AnotherTestEntity testEntity;
|
||||||
|
|
||||||
|
@OneToMany
|
||||||
|
@JoinTable(
|
||||||
|
name = TableCheckConstraintTest.ONE_TO_MANY_JOIN_TABLE_NAME,
|
||||||
|
check = {
|
||||||
|
@CheckConstraint(
|
||||||
|
name = "ONE_TO_MANY_JOIN_TABLE_CONSTRAINT",
|
||||||
|
constraint = TableCheckConstraintTest.ONE_TO_MANY_JOIN_TABLE_CONSTRAINTS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
private List<AnotherTestEntity> testEntities;
|
||||||
|
|
||||||
|
@ManyToMany
|
||||||
|
@JoinTable(
|
||||||
|
name = TableCheckConstraintTest.MANY_TO_MANY_JOIN_TABLE_NAME,
|
||||||
|
check = {
|
||||||
|
@CheckConstraint(
|
||||||
|
name = "MANY_TO_MANY_JOIN_TABLE_CONSTRAINT",
|
||||||
|
constraint = TableCheckConstraintTest.MANY_TO_MANY_JOIN_TABLE_CONSTRAINTS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
private List<AnotherTestEntity> testEntities2;
|
||||||
|
|
||||||
|
@Any
|
||||||
|
@AnyDiscriminator(DiscriminatorType.STRING)
|
||||||
|
@AnyDiscriminatorValues({
|
||||||
|
@AnyDiscriminatorValue(discriminator = "S", entity = AnotherTestEntity.class),
|
||||||
|
})
|
||||||
|
@AnyKeyJavaClass(Long.class)
|
||||||
|
@Column(name = "another_type")
|
||||||
|
@JoinColumn(name = "another_id")
|
||||||
|
@JoinTable(
|
||||||
|
name = TableCheckConstraintTest.ANY_JOIN_TABLE_NAME,
|
||||||
|
check = {
|
||||||
|
@CheckConstraint(
|
||||||
|
name = "ANY_JOIN_TABLE_CONSTRAINT",
|
||||||
|
constraint = TableCheckConstraintTest.ANY_JOIN_TABLE_CONSTRAINTS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
private Another another;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
package org.hibernate.orm.test.schemaupdate.checkconstraint.table;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.hibernate.boot.MetadataSources;
|
||||||
|
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||||
|
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||||
|
import org.hibernate.boot.spi.MetadataImplementor;
|
||||||
|
import org.hibernate.tool.hbm2ddl.SchemaExport;
|
||||||
|
import org.hibernate.tool.schema.TargetType;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.BaseUnitTest;
|
||||||
|
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
|
||||||
|
import org.hibernate.testing.orm.junit.JiraKey;
|
||||||
|
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
|
||||||
|
import org.hibernate.testing.util.ServiceRegistryUtil;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@BaseUnitTest
|
||||||
|
@JiraKey("HHH-18054")
|
||||||
|
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsColumnCheck.class)
|
||||||
|
public class TableCheckConstraintTest {
|
||||||
|
static final String CONSTRAINTS = "NAME_COLUMN <> null";
|
||||||
|
static final String SECONDARY_TABLE_CONSTRAINTS = "SECOND_NAME <> null";
|
||||||
|
static final String ONE_TO_ONE_JOIN_TABLE_CONSTRAINTS = "ID <> null";
|
||||||
|
static final String ONE_TO_MANY_JOIN_TABLE_CONSTRAINTS = "ID > 2";
|
||||||
|
static final String MANY_TO_ONE_JOIN_TABLE_CONSTRAINTS = "ID > 3 ";
|
||||||
|
static final String MANY_TO_MANY_JOIN_TABLE_CONSTRAINTS = "ID > 4";
|
||||||
|
static final String ANY_JOIN_TABLE_CONSTRAINTS = "ID > 5";
|
||||||
|
|
||||||
|
static final String SECONDARY_TABLE_NAME = "SECOND_TABLE_NAME";
|
||||||
|
static final String ONE_TO_ONE_JOIN_TABLE_NAME = "ONE_TO_ONE_JOIN_TABLE_NAME";
|
||||||
|
static final String ONE_TO_MANY_JOIN_TABLE_NAME = "ONE_TO_MAIN_JOIN_TABLE_NAME";
|
||||||
|
static final String MANY_TO_ONE_JOIN_TABLE_NAME = "MANY_TO_ONE_JOIN_TABLE_NAME";
|
||||||
|
static final String MANY_TO_MANY_JOIN_TABLE_NAME = "MANY_TO_MANY_JOIN_TABLE_NAME";
|
||||||
|
static final String ANY_JOIN_TABLE_NAME = "ANY_JOIN_TABLE_NAME";
|
||||||
|
|
||||||
|
private File output;
|
||||||
|
private StandardServiceRegistry ssr;
|
||||||
|
private MetadataImplementor metadata;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() throws IOException {
|
||||||
|
output = File.createTempFile( "update_script", ".sql" );
|
||||||
|
output.deleteOnExit();
|
||||||
|
ssr = ServiceRegistryUtil.serviceRegistry();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void tearsDown() {
|
||||||
|
output.delete();
|
||||||
|
StandardServiceRegistryBuilder.destroy( ssr );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTableConstraintsAreApplied() throws Exception {
|
||||||
|
createSchema( TestEntity.class );
|
||||||
|
String fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase()
|
||||||
|
.replace( System.lineSeparator(), "" );
|
||||||
|
assertThat( fileContent.toUpperCase( Locale.ROOT ) ).contains( CONSTRAINTS.toUpperCase( Locale.ROOT ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSecondaryTableConstraintsAreApplied() throws Exception {
|
||||||
|
createSchema( EntityWithSecondaryTables.class, AnotherTestEntity.class );
|
||||||
|
String[] fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase()
|
||||||
|
.split( System.lineSeparator() );
|
||||||
|
assertTrue( tableCreationStatementContainsConstraints(
|
||||||
|
fileContent,
|
||||||
|
SECONDARY_TABLE_NAME,
|
||||||
|
SECONDARY_TABLE_CONSTRAINTS
|
||||||
|
), "Check Constraints on secondary table have not been created" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJoinTableConstraintsAreApplied() throws Exception {
|
||||||
|
createSchema( EntityWithSecondaryTables.class, AnotherTestEntity.class );
|
||||||
|
String[] fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase()
|
||||||
|
.split( System.lineSeparator() );
|
||||||
|
assertTrue( tableCreationStatementContainsConstraints(
|
||||||
|
fileContent,
|
||||||
|
MANY_TO_ONE_JOIN_TABLE_NAME,
|
||||||
|
MANY_TO_ONE_JOIN_TABLE_CONSTRAINTS
|
||||||
|
), "Check Constraints on ManyToOne join table have not been created" );
|
||||||
|
assertTrue( tableCreationStatementContainsConstraints(
|
||||||
|
fileContent,
|
||||||
|
MANY_TO_MANY_JOIN_TABLE_NAME,
|
||||||
|
MANY_TO_MANY_JOIN_TABLE_CONSTRAINTS
|
||||||
|
), "Check Constraints on ManyToMany join table have not been created" );
|
||||||
|
assertTrue( tableCreationStatementContainsConstraints(
|
||||||
|
fileContent,
|
||||||
|
ONE_TO_ONE_JOIN_TABLE_NAME,
|
||||||
|
ONE_TO_ONE_JOIN_TABLE_CONSTRAINTS
|
||||||
|
), "Check Constraints on OneToOne join table have not been created" );
|
||||||
|
assertTrue( tableCreationStatementContainsConstraints(
|
||||||
|
fileContent,
|
||||||
|
ONE_TO_MANY_JOIN_TABLE_NAME,
|
||||||
|
ONE_TO_MANY_JOIN_TABLE_CONSTRAINTS
|
||||||
|
), "Check Constraints on OneToOne join table have not been created" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAnyJoinTableConstraintsAreApplied() throws Exception {
|
||||||
|
createSchema( EntityWithSecondaryTables.class, AnotherTestEntity.class );
|
||||||
|
String[] fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase()
|
||||||
|
.split( System.lineSeparator() );
|
||||||
|
assertTrue( tableCreationStatementContainsConstraints(
|
||||||
|
fileContent,
|
||||||
|
ANY_JOIN_TABLE_NAME,
|
||||||
|
ANY_JOIN_TABLE_CONSTRAINTS
|
||||||
|
), "Check Constraints on Any join table have not been created" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean tableCreationStatementContainsConstraints(
|
||||||
|
String[] fileContent,
|
||||||
|
String tableName,
|
||||||
|
String secondaryTableConstraints) {
|
||||||
|
for ( int i = 0; i < fileContent.length; i++ ) {
|
||||||
|
String statement = fileContent[i].toUpperCase( Locale.ROOT );
|
||||||
|
if ( statement.contains( "CREATE TABLE " + tableName.toUpperCase( Locale.ROOT ) ) ) {
|
||||||
|
if ( statement.contains( secondaryTableConstraints.toUpperCase( Locale.ROOT ) ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createSchema(Class... annotatedClasses) {
|
||||||
|
final MetadataSources metadataSources = new MetadataSources( ssr );
|
||||||
|
|
||||||
|
for ( Class c : annotatedClasses ) {
|
||||||
|
metadataSources.addAnnotatedClass( c );
|
||||||
|
}
|
||||||
|
metadata = (MetadataImplementor) metadataSources.buildMetadata();
|
||||||
|
metadata.orderColumns( false );
|
||||||
|
metadata.validate();
|
||||||
|
new SchemaExport()
|
||||||
|
.setHaltOnError( true )
|
||||||
|
.setOutputFile( output.getAbsolutePath() )
|
||||||
|
.setFormat( false )
|
||||||
|
.create( EnumSet.of( TargetType.SCRIPT ), metadata );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package org.hibernate.orm.test.schemaupdate.checkconstraint.table;
|
||||||
|
|
||||||
|
import jakarta.persistence.CheckConstraint;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
@Entity(name = "TEST_ENTITY_1")
|
||||||
|
@Table(
|
||||||
|
name = "TEST_ENTITY_TABLE",
|
||||||
|
check = {
|
||||||
|
@CheckConstraint(
|
||||||
|
name = "TABLE_CONSTRAINT",
|
||||||
|
constraint = TableCheckConstraintTest.CONSTRAINTS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public class TestEntity {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "NAME_COLUMN")
|
||||||
|
private String name;
|
||||||
|
}
|
Loading…
Reference in New Issue