19
19
import org .hibernate .type .Type ;
20
20
21
21
import static org .hibernate .engine .spi .CascadingActions .getLoadedElementsIterator ;
22
+ import static org .hibernate .internal .util .StringHelper .qualify ;
22
23
23
24
/**
24
25
* Implements the algorithm for validating property values for illegal null values
28
29
public final class Nullability {
29
30
private final SharedSessionContractImplementor session ;
30
31
private final boolean checkNullability ;
32
+ private NullabilityCheckType checkType ;
31
33
32
- /**
33
- * Constructs a Nullability
34
- *
35
- * @param session The session
36
- */
34
+ public enum NullabilityCheckType {
35
+ CREATE ,
36
+ UPDATE ,
37
+ DELETE
38
+ }
39
+
40
+ public Nullability (SharedSessionContractImplementor session , NullabilityCheckType checkType ) {
41
+ this .session = session ;
42
+ this .checkNullability = session .getFactory ().getSessionFactoryOptions ().isCheckNullability ();
43
+ this .checkType = checkType ;
44
+ }
45
+
46
+ @ Deprecated (forRemoval = true , since = "7" )
37
47
public Nullability (SharedSessionContractImplementor session ) {
38
48
this .session = session ;
39
49
this .checkNullability = session .getFactory ().getSessionFactoryOptions ().isCheckNullability ();
40
50
}
51
+
41
52
/**
42
- * Check nullability of the class persister properties
53
+ * Check nullability of the entity properties
43
54
*
44
55
* @param values entity properties
45
56
* @param persister class persister
46
57
* @param isUpdate whether it is intended to be updated or saved
47
58
*
48
59
* @throws PropertyValueException Break the nullability of one property
49
60
* @throws HibernateException error while getting Component values
61
+ *
62
+ * @deprecated Use {@link #checkNullability(Object[], EntityPersister)}
50
63
*/
64
+ @ Deprecated (forRemoval = true , since = "7" )
51
65
public void checkNullability (
52
66
final Object [] values ,
53
67
final EntityPersister persister ,
54
68
final boolean isUpdate ) {
55
- checkNullability ( values , persister , isUpdate ? NullabilityCheckType .UPDATE : NullabilityCheckType .CREATE );
69
+ checkType = isUpdate ? NullabilityCheckType .UPDATE : NullabilityCheckType .CREATE ;
70
+ checkNullability ( values , persister );
56
71
}
57
72
58
- public enum NullabilityCheckType {
59
- CREATE ,
60
- UPDATE ,
61
- DELETE
62
- }
63
-
64
- public void checkNullability (
65
- final Object [] values ,
66
- final EntityPersister persister ,
67
- final NullabilityCheckType checkType ) {
73
+ /**
74
+ * Check nullability of the entity properties
75
+ *
76
+ * @param values entity properties
77
+ * @param persister class persister
78
+ *
79
+ * @throws PropertyValueException Break the nullability of one property
80
+ * @throws HibernateException error while getting Component values
81
+ */
82
+ public void checkNullability ( final Object [] values , final EntityPersister persister ) {
68
83
69
- /*
70
- * Typically when Bean Validation is on, we don't want to validate null values
71
- * at the Hibernate Core level. Hence the checkNullability setting.
72
- */
84
+ // Typically when Bean Validation is on, we don't want to validate null values
85
+ // at the Hibernate Core level. Hence, the checkNullability setting.
73
86
if ( checkNullability ) {
74
- /*
75
- * Algorithm
76
- * Check for any level one nullability breaks
77
- * Look at non-null components to
78
- * recursively check next level of nullability breaks
79
- * Look at Collections containing components to
80
- * recursively check next level of nullability breaks
81
- *
82
- *
83
- * In the previous implementation, not-null stuffs where checked
84
- * filtering by level one only updatable
85
- * or insertable columns. So setting a subcomponent as update="false"
86
- * has no effect on not-null check if the main component had good checkability
87
- * In this implementation, we keep this feature.
88
- * However, I never see any documentation mentioning that, but it's for
89
- * sure a limitation.
90
- */
87
+ // Algorithm:
88
+ // Check for any level one nullability breaks
89
+ // Look at non-null components to
90
+ // recursively check next level of nullability breaks
91
+ // Look at Collections containing components to
92
+ // recursively check next level of nullability breaks
93
+ //
94
+ // In the previous implementation, not-null stuffs where checked
95
+ // filtering by level one only updatable
96
+ // or insertable columns. So setting a subcomponent as update="false"
97
+ // has no effect on not-null check if the main component had good checkability
98
+ // In this implementation, we keep this feature.
99
+ // However, I never see any documentation mentioning that, but it's for
100
+ // sure a limitation.
91
101
92
102
final boolean [] nullability = persister .getPropertyNullability ();
93
103
final boolean [] checkability = checkType == NullabilityCheckType .CREATE
94
104
? persister .getPropertyInsertability ()
95
105
: persister .getPropertyUpdateability ();
96
106
final Type [] propertyTypes = persister .getPropertyTypes ();
97
107
final Generator [] generators = persister .getEntityMetamodel ().getGenerators ();
98
-
99
108
for ( int i = 0 ; i < values .length ; i ++ ) {
100
-
101
109
if ( checkability [i ]
102
110
&& values [i ] != LazyPropertyInitializer .UNFETCHED_PROPERTY
103
111
&& !generated ( generators [i ] ) ) {
@@ -118,13 +126,12 @@ else if ( value != null ) {
118
126
throw new PropertyValueException (
119
127
"not-null property references a null or transient value" ,
120
128
persister .getEntityName (),
121
- buildPropertyPath ( persister .getPropertyNames ()[i ], breakProperties )
129
+ qualify ( persister .getPropertyNames ()[i ], breakProperties )
122
130
);
123
131
}
124
132
125
133
}
126
134
}
127
-
128
135
}
129
136
}
130
137
}
@@ -134,98 +141,90 @@ private static boolean generated(Generator generator) {
134
141
}
135
142
136
143
/**
137
- * check sub elements- nullability. Returns property path that break
138
- * nullability or null if none
144
+ * Check nullability of sub-elements.
145
+ * Returns property path that break nullability, or null if none.
139
146
*
140
147
* @param propertyType type to check
141
148
* @param value value to check
142
149
*
143
150
* @return property path
144
151
* @throws HibernateException error while getting subcomponent values
145
152
*/
146
- private String checkSubElementsNullability (Type propertyType , Object value ) throws HibernateException {
147
- if ( propertyType instanceof AnyType ) {
148
- return checkComponentNullability ( value , ( AnyType ) propertyType );
153
+ private String checkSubElementsNullability (Type propertyType , Object value ) {
154
+ if ( propertyType instanceof AnyType anyType ) {
155
+ return checkComponentNullability ( value , anyType );
149
156
}
150
- if ( propertyType instanceof ComponentType ) {
151
- return checkComponentNullability ( value , ( ComponentType ) propertyType );
157
+ else if ( propertyType instanceof ComponentType componentType ) {
158
+ return checkComponentNullability ( value , componentType );
152
159
}
153
-
154
- if ( propertyType instanceof CollectionType collectionType ) {
160
+ else if ( propertyType instanceof CollectionType collectionType ) {
155
161
// persistent collections may have components
156
- final Type collectionElementType = collectionType .getElementType ( session .getFactory () );
157
-
158
- if ( collectionElementType instanceof ComponentType || collectionElementType instanceof AnyType ) {
162
+ if ( collectionType .getElementType ( session .getFactory () ) instanceof CompositeType componentType ) {
159
163
// check for all components values in the collection
160
- final CompositeType componentType = (CompositeType ) collectionElementType ;
161
- final Iterator <?> itr = getLoadedElementsIterator ( session , collectionType , value );
162
- while ( itr .hasNext () ) {
163
- final Object compositeElement = itr .next ();
164
+ final Iterator <?> iterator = getLoadedElementsIterator ( collectionType , value );
165
+ while ( iterator .hasNext () ) {
166
+ final Object compositeElement = iterator .next ();
164
167
if ( compositeElement != null ) {
165
- return checkComponentNullability ( compositeElement , componentType );
168
+ final String path = checkComponentNullability ( compositeElement , componentType );
169
+ if ( path != null ) {
170
+ return path ;
171
+ }
166
172
}
167
173
}
168
174
}
175
+ return null ;
176
+ }
177
+ else {
178
+ return null ;
169
179
}
170
-
171
- return null ;
172
180
}
173
181
174
182
/**
175
- * check component nullability. Returns property path that break
176
- * nullability or null if none
183
+ * Check component nullability.
184
+ * Returns property path that breaks nullability, or null if none.
177
185
*
178
- * @param value component properties
186
+ * @param composite component properties
179
187
* @param compositeType component not-nullable type
180
188
*
181
189
* @return property path
182
190
* @throws HibernateException error while getting subcomponent values
183
191
*/
184
- private String checkComponentNullability (Object value , CompositeType compositeType ) throws HibernateException {
192
+ private String checkComponentNullability (Object composite , CompositeType compositeType ) {
185
193
// IMPL NOTE : we currently skip checking "any" and "many to any" mappings.
186
194
//
187
- // This is not the best solution. But atm there is a mismatch between AnyType#getPropertyNullability
195
+ // This is not the best solution. But atm there is a mismatch between AnyType#getPropertyNullability
188
196
// and the fact that cascaded-saves for "many to any" mappings are not performed until after this nullability
189
- // check. So the nullability check fails for transient entity elements with generated identifiers because
197
+ // check. So the nullability check fails for transient entity elements with generated identifiers because
190
198
// the identifier is not yet generated/assigned (is null)
191
199
//
192
200
// The more correct fix would be to cascade saves of the many-to-any elements before the Nullability checking
193
201
194
202
if ( compositeType instanceof AnyType ) {
195
203
return null ;
196
204
}
197
-
198
- final boolean [] nullability = compositeType .getPropertyNullability ();
199
- if ( nullability != null ) {
200
- //do the test
201
- final Object [] subValues = compositeType .getPropertyValues ( value , session );
202
- final Type [] propertyTypes = compositeType .getSubtypes ();
203
- for ( int i = 0 ; i < subValues .length ; i ++ ) {
204
- final Object subValue = subValues [i ];
205
- if ( !nullability [i ] && subValue ==null ) {
206
- return compositeType .getPropertyNames ()[i ];
207
- }
208
- else if ( subValue != null ) {
209
- final String breakProperties = checkSubElementsNullability ( propertyTypes [i ], subValue );
210
- if ( breakProperties != null ) {
211
- return buildPropertyPath ( compositeType .getPropertyNames ()[i ], breakProperties );
205
+ else {
206
+ final boolean [] nullability = compositeType .getPropertyNullability ();
207
+ if ( nullability != null ) {
208
+ //do the test
209
+ final Object [] values = compositeType .getPropertyValues ( composite , session );
210
+ final Type [] propertyTypes = compositeType .getSubtypes ();
211
+ for ( int i = 0 ; i < values .length ; i ++ ) {
212
+ final Object value = values [i ];
213
+ if ( value == null ) {
214
+ if ( !nullability [i ] ) {
215
+ return compositeType .getPropertyNames ()[i ];
216
+ }
217
+ }
218
+ else {
219
+ final String breakProperties = checkSubElementsNullability ( propertyTypes [i ], value );
220
+ if ( breakProperties != null ) {
221
+ return qualify ( compositeType .getPropertyNames ()[i ], breakProperties );
222
+ }
212
223
}
213
224
}
214
225
}
226
+ return null ;
215
227
}
216
- return null ;
217
- }
218
-
219
- /**
220
- * Return a well formed property path. Basically, it will return parent.child
221
- *
222
- * @param parent parent in path
223
- * @param child child in path
224
- *
225
- * @return parent-child path
226
- */
227
- private static String buildPropertyPath (String parent , String child ) {
228
- return parent + '.' + child ;
229
228
}
230
229
231
230
}
0 commit comments