Skip to content

Commit 37d57cd

Browse files
committed
HHH-19614 generate CHECK constraint to enforce non-nullability in SINGLE_TABLE inheritance
1 parent 5d10380 commit 37d57cd

File tree

5 files changed

+75
-12
lines changed

5 files changed

+75
-12
lines changed

hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,9 @@ public static void bindEntityClass(
219219
assert superEntity != null;
220220
superEntity.addSubclass( subclass );
221221
}
222+
223+
persistentClass.createConstraints( context );
224+
222225
collector.addEntityBinding( persistentClass );
223226
// process secondary tables and complementary definitions (ie o.h.a.Table)
224227
collector.addSecondPass( new SecondaryTableFromAnnotationSecondPass( entityBinder, holder ) );

hibernate-core/src/main/java/org/hibernate/mapping/Column.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.hibernate.boot.Metadata;
1717
import org.hibernate.boot.model.naming.Identifier;
1818
import org.hibernate.boot.model.relational.Database;
19+
import org.hibernate.boot.spi.InFlightMetadataCollector;
1920
import org.hibernate.boot.spi.MetadataBuildingContext;
2021
import org.hibernate.dialect.Dialect;
2122
import org.hibernate.engine.jdbc.Size;
@@ -117,7 +118,11 @@ public void setValue(Value value) {
117118
}
118119

119120
public JdbcMapping getType() {
120-
return getValue().getSelectableType( getValue().getBuildingContext().getMetadataCollector(), getTypeIndex() );
121+
return getValue().getSelectableType( getMetadataCollector(), getTypeIndex() );
122+
}
123+
124+
private InFlightMetadataCollector getMetadataCollector() {
125+
return getValue().getBuildingContext().getMetadataCollector();
121126
}
122127

123128
public String getName() {
@@ -176,22 +181,16 @@ private static boolean isCloseQuote(char ch) {
176181
* @return the quoted name as it would occur in the mapping file
177182
*/
178183
public String getQuotedName() {
179-
return safeInterning(
180-
quoted ?
181-
"`" + name + "`" :
182-
name
183-
);
184+
return safeInterning( quoted ? "`" + name + "`" : name );
184185
}
185186

186187
/**
187188
* @return the quoted name using the quoting syntax of the given dialect
188189
*/
189190
public String getQuotedName(Dialect dialect) {
190-
return safeInterning(
191-
quoted ?
192-
dialect.openQuote() + name + dialect.closeQuote() :
193-
name
194-
);
191+
return safeInterning( quoted
192+
? dialect.openQuote() + name + dialect.closeQuote()
193+
: name );
195194
}
196195

197196
@Override

hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,4 +1230,7 @@ public void removeProperty(Property property) {
12301230
}
12311231
properties.remove( property );
12321232
}
1233+
1234+
public void createConstraints(MetadataBuildingContext context) {
1235+
}
12331236
}

hibernate-core/src/main/java/org/hibernate/mapping/Property.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,10 @@ public Generator createGenerator(RuntimeModelCreationContext context) {
496496
}
497497

498498
public Property copy() {
499-
final Property property = this instanceof SyntheticProperty ? new SyntheticProperty() : new Property();
499+
final Property property =
500+
this instanceof SyntheticProperty
501+
? new SyntheticProperty()
502+
: new Property();
500503
property.setName( getName() );
501504
property.setValue( getValue() );
502505
property.setCascade( getCascade() );

hibernate-core/src/main/java/org/hibernate/mapping/SingleTableSubclass.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@
99
import org.hibernate.MappingException;
1010
import org.hibernate.boot.Metadata;
1111
import org.hibernate.boot.spi.MetadataBuildingContext;
12+
import org.hibernate.dialect.Dialect;
1213
import org.hibernate.internal.util.collections.JoinedList;
1314

15+
import static org.hibernate.persister.entity.DiscriminatorHelper.getDiscriminatorSQLValue;
16+
17+
1418
/**
1519
* A mapping model object that represents a subclass in a
1620
* {@linkplain jakarta.persistence.InheritanceType#SINGLE_TABLE single table}
@@ -40,4 +44,55 @@ public void validate(Metadata mapping) throws MappingException {
4044
}
4145
super.validate( mapping );
4246
}
47+
48+
@Override
49+
public void createConstraints(MetadataBuildingContext context) {
50+
if ( !isAbstract() ) {
51+
final Dialect dialect = context.getMetadataCollector().getDatabase().getDialect();
52+
if ( dialect.supportsTableCheck() ) {
53+
final Value discriminator = getDiscriminator();
54+
final List<Selectable> selectables = discriminator.getSelectables();
55+
if ( selectables.size() == 1 ) {
56+
final StringBuilder check = new StringBuilder();
57+
check.append( selectables.get( 0 ).getText( dialect ) );
58+
if ( isDiscriminatorValueNull() ) {
59+
check.append( " is " );
60+
}
61+
else if ( isDiscriminatorValueNotNull() ) {
62+
check.append( " is " );
63+
// can't enforce this for now, because 'not null'
64+
// really means "not null and not any of the other
65+
// explicitly listed values"
66+
return; //ABORT
67+
}
68+
else {
69+
check.append( " <> " );
70+
}
71+
check.append( getDiscriminatorSQLValue( this, dialect ) )
72+
.append( " or (" );
73+
boolean first = true;
74+
for ( Property property : getNonDuplicatedProperties() ) {
75+
if ( !property.isComposite() && !property.isOptional() ) {
76+
for ( Selectable selectable : property.getSelectables() ) {
77+
if ( selectable instanceof Column column && column.isNullable() ) {
78+
if ( first ) {
79+
first = false;
80+
}
81+
else {
82+
check.append( " and " );
83+
}
84+
check.append( column.getQuotedName( dialect ) )
85+
.append( " is not null" );
86+
}
87+
}
88+
}
89+
}
90+
check.append( ")" );
91+
if ( !first ) {
92+
getTable().addCheck( new CheckConstraint( check.toString() ) );
93+
}
94+
}
95+
}
96+
}
97+
}
4398
}

0 commit comments

Comments
 (0)