diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java index 7f4855223789..3e8b8aa35dbf 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java @@ -219,6 +219,9 @@ public static void bindEntityClass( assert superEntity != null; superEntity.addSubclass( subclass ); } + + persistentClass.createConstraints( context ); + collector.addEntityBinding( persistentClass ); // process secondary tables and complementary definitions (ie o.h.a.Table) collector.addSecondPass( new SecondaryTableFromAnnotationSecondPass( entityBinder, holder ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Column.java b/hibernate-core/src/main/java/org/hibernate/mapping/Column.java index 7adcf534a921..dd0c5c9f3f59 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Column.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Column.java @@ -16,6 +16,7 @@ import org.hibernate.boot.Metadata; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.relational.Database; +import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.Size; @@ -117,7 +118,11 @@ public void setValue(Value value) { } public JdbcMapping getType() { - return getValue().getSelectableType( getValue().getBuildingContext().getMetadataCollector(), getTypeIndex() ); + return getValue().getSelectableType( getMetadataCollector(), getTypeIndex() ); + } + + private InFlightMetadataCollector getMetadataCollector() { + return getValue().getBuildingContext().getMetadataCollector(); } public String getName() { @@ -176,22 +181,16 @@ private static boolean isCloseQuote(char ch) { * @return the quoted name as it would occur in the mapping file */ public String getQuotedName() { - return safeInterning( - quoted ? - "`" + name + "`" : - name - ); + return safeInterning( quoted ? "`" + name + "`" : name ); } /** * @return the quoted name using the quoting syntax of the given dialect */ public String getQuotedName(Dialect dialect) { - return safeInterning( - quoted ? - dialect.openQuote() + name + dialect.closeQuote() : - name - ); + return safeInterning( quoted + ? dialect.openQuote() + name + dialect.closeQuote() + : name ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java index 7af9f69cce1b..acdae60bedb8 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java @@ -1230,4 +1230,7 @@ public void removeProperty(Property property) { } properties.remove( property ); } + + public void createConstraints(MetadataBuildingContext context) { + } } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Property.java b/hibernate-core/src/main/java/org/hibernate/mapping/Property.java index 91fdcc69f1a2..25efe81823e7 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Property.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Property.java @@ -496,7 +496,10 @@ public Generator createGenerator(RuntimeModelCreationContext context) { } public Property copy() { - final Property property = this instanceof SyntheticProperty ? new SyntheticProperty() : new Property(); + final Property property = + this instanceof SyntheticProperty + ? new SyntheticProperty() + : new Property(); property.setName( getName() ); property.setValue( getValue() ); property.setCascade( getCascade() ); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/SingleTableSubclass.java b/hibernate-core/src/main/java/org/hibernate/mapping/SingleTableSubclass.java index c9288381d7ba..aeb01a302c10 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/SingleTableSubclass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/SingleTableSubclass.java @@ -9,8 +9,12 @@ import org.hibernate.MappingException; import org.hibernate.boot.Metadata; import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.dialect.Dialect; import org.hibernate.internal.util.collections.JoinedList; +import static org.hibernate.persister.entity.DiscriminatorHelper.getDiscriminatorSQLValue; + + /** * A mapping model object that represents a subclass in a * {@linkplain jakarta.persistence.InheritanceType#SINGLE_TABLE single table} @@ -40,4 +44,55 @@ public void validate(Metadata mapping) throws MappingException { } super.validate( mapping ); } + + @Override + public void createConstraints(MetadataBuildingContext context) { + if ( !isAbstract() ) { + final Dialect dialect = context.getMetadataCollector().getDatabase().getDialect(); + if ( dialect.supportsTableCheck() ) { + final Value discriminator = getDiscriminator(); + final List selectables = discriminator.getSelectables(); + if ( selectables.size() == 1 ) { + final StringBuilder check = new StringBuilder(); + check.append( selectables.get( 0 ).getText( dialect ) ); + if ( isDiscriminatorValueNull() ) { + check.append( " is " ); + } + else if ( isDiscriminatorValueNotNull() ) { + check.append( " is " ); + // can't enforce this for now, because 'not null' + // really means "not null and not any of the other + // explicitly listed values" + return; //ABORT + } + else { + check.append( " <> " ); + } + check.append( getDiscriminatorSQLValue( this, dialect ) ) + .append( " or (" ); + boolean first = true; + for ( Property property : getNonDuplicatedProperties() ) { + if ( !property.isComposite() && !property.isOptional() ) { + for ( Selectable selectable : property.getSelectables() ) { + if ( selectable instanceof Column column && column.isNullable() ) { + if ( first ) { + first = false; + } + else { + check.append( " and " ); + } + check.append( column.getQuotedName( dialect ) ) + .append( " is not null" ); + } + } + } + } + check.append( ")" ); + if ( !first ) { + getTable().addCheck( new CheckConstraint( check.toString() ) ); + } + } + } + } + } }