Skip to content

Commit 22b23af

Browse files
committed
HHH-19614 generate CHECK constraint to enforce non-nullability in SINGLE_TABLE inheritance
1 parent a5bab86 commit 22b23af

File tree

5 files changed

+81
-12
lines changed

5 files changed

+81
-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: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
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+
16+
1417
/**
1518
* A mapping model object that represents a subclass in a
1619
* {@linkplain jakarta.persistence.InheritanceType#SINGLE_TABLE single table}
@@ -40,4 +43,62 @@ public void validate(Metadata mapping) throws MappingException {
4043
}
4144
super.validate( mapping );
4245
}
46+
47+
@Override
48+
public void createConstraints(MetadataBuildingContext context) {
49+
if ( !isAbstract() ) {
50+
final Dialect dialect = context.getMetadataCollector().getDatabase().getDialect();
51+
if ( dialect.supportsTableCheck() ) {
52+
final List<Selectable> discriminator = getDiscriminator().getSelectables();
53+
if ( discriminator.size() == 1
54+
&& discriminator.get( 0 ) instanceof Column discriminatorColumn ) {
55+
final StringBuilder check = new StringBuilder();
56+
check.append( discriminatorColumn.getQuotedName( dialect ) );
57+
if ( isDiscriminatorValueNull() ) {
58+
check.append( " is not null" );
59+
}
60+
if ( isDiscriminatorValueNotNull() ) {
61+
check.append( " is null" );
62+
// can't enforce this for now, because 'not null'
63+
// really means "not null and not any of the other
64+
// explicitly listed values"
65+
return; //ABORT
66+
}
67+
else {
68+
check.append( " <> " );
69+
final boolean quote = discriminatorColumn.getType().getJdbcType().isString();
70+
if ( quote ) {
71+
check.append( "'" );
72+
}
73+
check.append( getDiscriminatorValue() );
74+
if ( quote ) {
75+
check.append( "'" );
76+
}
77+
}
78+
check.append( " or (" );
79+
boolean first = true;
80+
for ( Property property : getNonDuplicatedProperties() ) {
81+
if ( !property.isComposite() && !property.isOptional() ) {
82+
for ( Selectable selectable : property.getSelectables() ) {
83+
if ( selectable instanceof Column column && column.isNullable() ) {
84+
if ( first ) {
85+
first = false;
86+
}
87+
else {
88+
check.append( " and " );
89+
}
90+
check.append( column.getQuotedName( dialect ) )
91+
.append( " is not null" );
92+
}
93+
}
94+
}
95+
}
96+
check.append( ")" );
97+
if ( !first ) {
98+
getTable().addCheck( new CheckConstraint( check.toString() ) );
99+
}
100+
}
101+
}
102+
}
103+
}
43104
}

0 commit comments

Comments
 (0)