Skip to content

Commit 1c290b8

Browse files
committed
move away from magical unsaved-value strings
introduce NullValueSemantic Signed-off-by: Gavin King <[email protected]>
1 parent 357b275 commit 1c290b8

File tree

5 files changed

+120
-50
lines changed

5 files changed

+120
-50
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ private Component getOrCreateCompositeId(RootClass rootClass) {
385385
buildingContext
386386
);
387387
rootClass.setIdentifier( identifier );
388-
identifier.setNullValue( "undefined" );
388+
identifier.setNullValueUndefined();
389389
rootClass.setEmbeddedIdentifier( true );
390390
rootClass.setIdentifierMapper( identifier );
391391
return identifier;

hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -923,7 +923,7 @@ private void bindEntityVersion(
923923
versionValue.setNullValue( versionAttributeSource.getUnsavedValue() );
924924
}
925925
else {
926-
versionValue.setNullValue( "undefined" );
926+
versionValue.setNullValueUndefined();
927927
}
928928
if ( versionAttributeSource.getSource().equals("db") ) {
929929
property.setValueGeneratorCreator(

hibernate-core/src/main/java/org/hibernate/engine/internal/UnsavedValueFactory.java

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66

77
import java.util.function.Supplier;
88

9-
import org.hibernate.MappingException;
109
import org.hibernate.engine.spi.IdentifierValue;
1110
import org.hibernate.engine.spi.VersionValue;
1211
import org.hibernate.mapping.KeyValue;
12+
import org.hibernate.mapping.KeyValue.NullValueSemantic;
1313
import org.hibernate.property.access.spi.Getter;
1414
import org.hibernate.type.descriptor.java.JavaType;
1515
import org.hibernate.type.descriptor.java.VersionJavaType;
@@ -35,28 +35,31 @@ public static IdentifierValue getUnsavedIdentifierValue(
3535
JavaType<?> idJavaType,
3636
Getter getter,
3737
Supplier<?> templateInstanceAccess) {
38-
final String unsavedValue = bootIdMapping.getNullValue();
39-
if ( unsavedValue == null ) {
40-
if ( getter != null && templateInstanceAccess != null ) {
41-
// use the id value of a newly instantiated instance as the unsaved-value
42-
final Object defaultValue = getter.get( templateInstanceAccess.get() );
43-
return new IdentifierValue( defaultValue );
44-
}
45-
else if ( idJavaType instanceof PrimitiveJavaType<?> primitiveJavaType ) {
46-
return new IdentifierValue( primitiveJavaType.getDefaultValue() );
47-
}
48-
else {
49-
return IdentifierValue.NULL;
50-
}
38+
final NullValueSemantic nullValueSemantic = bootIdMapping.getNullValueSemantic();
39+
return nullValueSemantic == null
40+
? inferUnsavedIdentifierValue( idJavaType, getter, templateInstanceAccess )
41+
: switch ( nullValueSemantic ) {
42+
case UNDEFINED -> IdentifierValue.UNDEFINED;
43+
case NULL -> IdentifierValue.NULL;
44+
case ANY -> IdentifierValue.ANY;
45+
case NONE -> IdentifierValue.NONE;
46+
case VALUE -> new IdentifierValue( idJavaType.fromString( bootIdMapping.getNullValue() ) );
47+
default -> throw new IllegalArgumentException( "Illegal null-value semantic: " + nullValueSemantic );
48+
};
49+
}
50+
51+
private static IdentifierValue inferUnsavedIdentifierValue(
52+
JavaType<?> idJavaType, Getter getter, Supplier<?> templateInstanceAccess) {
53+
if ( getter != null && templateInstanceAccess != null ) {
54+
// use the id value of a newly instantiated instance as the unsaved-value
55+
final Object defaultValue = getter.get( templateInstanceAccess.get() );
56+
return new IdentifierValue( defaultValue );
57+
}
58+
else if ( idJavaType instanceof PrimitiveJavaType<?> primitiveJavaType ) {
59+
return new IdentifierValue( primitiveJavaType.getDefaultValue() );
5160
}
5261
else {
53-
return switch ( unsavedValue ) {
54-
case "null" -> IdentifierValue.NULL;
55-
case "undefined" -> IdentifierValue.UNDEFINED;
56-
case "none" -> IdentifierValue.NONE;
57-
case "any" -> IdentifierValue.ANY;
58-
default -> new IdentifierValue( idJavaType.fromString( unsavedValue ) );
59-
};
62+
return IdentifierValue.NULL;
6063
}
6164
}
6265

@@ -72,31 +75,33 @@ public static <T> VersionValue getUnsavedVersionValue(
7275
VersionJavaType<T> versionJavaType,
7376
Getter getter,
7477
Supplier<?> templateInstanceAccess) {
75-
final String unsavedValue = bootVersionMapping.getNullValue();
76-
if ( unsavedValue == null ) {
77-
if ( getter != null && templateInstanceAccess != null ) {
78-
final Object defaultValue = getter.get( templateInstanceAccess.get() );
79-
// if the version of a newly instantiated object is null
80-
// or a negative number, use that value as the unsaved-value,
81-
// otherwise assume it's the initial version set by program
82-
return isNullInitialVersion( defaultValue )
83-
? new VersionValue( defaultValue )
84-
: VersionValue.UNDEFINED;
85-
}
86-
else {
87-
return VersionValue.UNDEFINED;
88-
}
78+
final NullValueSemantic nullValueSemantic = bootVersionMapping.getNullValueSemantic();
79+
return nullValueSemantic == null
80+
? inferUnsavedVersionValue( versionJavaType, getter, templateInstanceAccess )
81+
: switch ( nullValueSemantic ) {
82+
case UNDEFINED -> VersionValue.UNDEFINED;
83+
case NULL -> VersionValue.NULL;
84+
case NEGATIVE -> VersionValue.NEGATIVE;
85+
// this should not happen since the DTD prevents it
86+
case VALUE -> new VersionValue( versionJavaType.fromString( bootVersionMapping.getNullValue() ) );
87+
default -> throw new IllegalArgumentException( "Illegal null-value semantic: " + nullValueSemantic );
88+
};
89+
}
90+
91+
private static VersionValue inferUnsavedVersionValue(
92+
VersionJavaType<?> versionJavaType, Getter getter, Supplier<?> templateInstanceAccess) {
93+
if ( getter != null && templateInstanceAccess != null ) {
94+
final Object defaultValue = getter.get( templateInstanceAccess.get() );
95+
// if the version of a newly instantiated object is null
96+
// or a negative number, use that value as the unsaved-value,
97+
// otherwise assume it's the initial version set by program
98+
return isNullInitialVersion( defaultValue )
99+
? new VersionValue( defaultValue )
100+
: VersionValue.UNDEFINED;
89101
}
90102
else {
91-
// this should not happen since the DTD prevents it
92-
return switch ( unsavedValue ) {
93-
case "undefined" -> VersionValue.UNDEFINED;
94-
case "null" -> VersionValue.NULL;
95-
case "negative" -> VersionValue.NEGATIVE;
96-
default -> throw new MappingException( "Could not parse version unsaved-value: " + unsavedValue );
97-
};
103+
return VersionValue.UNDEFINED;
98104
}
99-
100105
}
101106

102107
private UnsavedValueFactory() {

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,18 @@ public interface KeyValue extends Value {
2222

2323
boolean isCascadeDeleteEnabled();
2424

25+
enum NullValueSemantic { VALUE, NULL, NEGATIVE, UNDEFINED, NONE, ANY }
26+
27+
NullValueSemantic getNullValueSemantic();
28+
2529
String getNullValue();
2630

2731
boolean isUpdateable();
2832

33+
/**
34+
* @deprecated No longer called, except from tests.
35+
* Use {@link #createGenerator(Dialect, RootClass, Property, GeneratorSettings)}
36+
*/
2937
@Deprecated(since = "7.0", forRemoval = true)
3038
Generator createGenerator(Dialect dialect, RootClass rootClass);
3139

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

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ public abstract class SimpleValue implements KeyValue {
9191
private boolean isNationalized;
9292
private boolean isLob;
9393

94+
private NullValueSemantic nullValueSemantic;
9495
private String nullValue;
9596

9697
private Table table;
@@ -410,8 +411,8 @@ public Generator createGenerator(
410411
final IdGeneratorCreationContext context =
411412
new IdGeneratorCreationContext( rootClass, property, defaults );
412413
final Generator generator = customIdGeneratorCreator.createGenerator( context );
413-
if ( generator.allowAssignedIdentifiers() && getNullValue() == null ) {
414-
setNullValue( "undefined" );
414+
if ( generator.allowAssignedIdentifiers() && nullValue == null ) {
415+
setNullValueUndefined();
415416
}
416417
return generator;
417418
}
@@ -449,17 +450,73 @@ public Table getTable() {
449450
return table;
450451
}
451452

453+
/**
454+
* The property or field value which indicates that field
455+
* or property has never been set.
456+
*
457+
* @see org.hibernate.engine.internal.UnsavedValueFactory
458+
* @see org.hibernate.engine.spi.IdentifierValue
459+
* @see org.hibernate.engine.spi.VersionValue
460+
*/
452461
@Override
453462
public String getNullValue() {
454463
return nullValue;
455464
}
456465

457466
/**
458-
* Sets the nullValue.
459-
* @param nullValue The nullValue to set
467+
* Set the property or field value indicating that field
468+
* or property has never been set.
469+
*
470+
* @see org.hibernate.engine.internal.UnsavedValueFactory
471+
* @see org.hibernate.engine.spi.IdentifierValue
472+
* @see org.hibernate.engine.spi.VersionValue
460473
*/
461474
public void setNullValue(String nullValue) {
462-
this.nullValue = nullValue;
475+
nullValueSemantic = switch (nullValue) {
476+
// magical values (legacy of hbm.xml)
477+
case "null" -> NullValueSemantic.NULL;
478+
case "none" -> NullValueSemantic.NONE;
479+
case "any" -> NullValueSemantic.ANY;
480+
case "undefined" -> NullValueSemantic.UNDEFINED;
481+
default -> NullValueSemantic.VALUE;
482+
};
483+
if ( nullValueSemantic == NullValueSemantic.VALUE ) {
484+
this.nullValue = nullValue;
485+
}
486+
}
487+
488+
/**
489+
* The rule for determining if the field or
490+
* property has been set.
491+
*
492+
* @see org.hibernate.engine.internal.UnsavedValueFactory
493+
*/
494+
@Override
495+
public NullValueSemantic getNullValueSemantic() {
496+
return nullValueSemantic;
497+
}
498+
499+
/**
500+
* Specifies the rule for determining if the field or
501+
* property has been set.
502+
*
503+
* @see org.hibernate.engine.internal.UnsavedValueFactory
504+
*/
505+
public void setNullValueSemantic(NullValueSemantic nullValueSemantic) {
506+
this.nullValueSemantic = nullValueSemantic;
507+
}
508+
509+
/**
510+
* Specifies that there is no well-defined property or
511+
* field value indicating that field or property has never
512+
* been set.
513+
*
514+
* @see org.hibernate.engine.internal.UnsavedValueFactory
515+
* @see org.hibernate.engine.spi.IdentifierValue#UNDEFINED
516+
* @see org.hibernate.engine.spi.VersionValue#UNDEFINED
517+
*/
518+
public void setNullValueUndefined() {
519+
nullValueSemantic = NullValueSemantic.UNDEFINED;
463520
}
464521

465522
public String getForeignKeyName() {

0 commit comments

Comments
 (0)