1919import org .hibernate .type .Type ;
2020
2121import static org .hibernate .engine .spi .CascadingActions .getLoadedElementsIterator ;
22+ import static org .hibernate .internal .util .StringHelper .qualify ;
2223
2324/**
2425 * Implements the algorithm for validating property values for illegal null values
2829public final class Nullability {
2930 private final SharedSessionContractImplementor session ;
3031 private final boolean checkNullability ;
32+ private NullabilityCheckType checkType ;
3133
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" )
3747 public Nullability (SharedSessionContractImplementor session ) {
3848 this .session = session ;
3949 this .checkNullability = session .getFactory ().getSessionFactoryOptions ().isCheckNullability ();
4050 }
51+
4152 /**
42- * Check nullability of the class persister properties
53+ * Check nullability of the entity properties
4354 *
4455 * @param values entity properties
4556 * @param persister class persister
4657 * @param isUpdate whether it is intended to be updated or saved
4758 *
4859 * @throws PropertyValueException Break the nullability of one property
4960 * @throws HibernateException error while getting Component values
61+ *
62+ * @deprecated Use {@link #checkNullability(Object[], EntityPersister)}
5063 */
64+ @ Deprecated (forRemoval = true , since = "7" )
5165 public void checkNullability (
5266 final Object [] values ,
5367 final EntityPersister persister ,
5468 final boolean isUpdate ) {
55- checkNullability ( values , persister , isUpdate ? NullabilityCheckType .UPDATE : NullabilityCheckType .CREATE );
69+ checkType = isUpdate ? NullabilityCheckType .UPDATE : NullabilityCheckType .CREATE ;
70+ checkNullability ( values , persister );
5671 }
5772
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 ) {
6883
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.
7386 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.
91101
92102 final boolean [] nullability = persister .getPropertyNullability ();
93103 final boolean [] checkability = checkType == NullabilityCheckType .CREATE
94104 ? persister .getPropertyInsertability ()
95105 : persister .getPropertyUpdateability ();
96106 final Type [] propertyTypes = persister .getPropertyTypes ();
97107 final Generator [] generators = persister .getEntityMetamodel ().getGenerators ();
98-
99108 for ( int i = 0 ; i < values .length ; i ++ ) {
100-
101109 if ( checkability [i ]
102110 && values [i ] != LazyPropertyInitializer .UNFETCHED_PROPERTY
103111 && !generated ( generators [i ] ) ) {
@@ -118,13 +126,12 @@ else if ( value != null ) {
118126 throw new PropertyValueException (
119127 "not-null property references a null or transient value" ,
120128 persister .getEntityName (),
121- buildPropertyPath ( persister .getPropertyNames ()[i ], breakProperties )
129+ qualify ( persister .getPropertyNames ()[i ], breakProperties )
122130 );
123131 }
124132
125133 }
126134 }
127-
128135 }
129136 }
130137 }
@@ -134,98 +141,90 @@ private static boolean generated(Generator generator) {
134141 }
135142
136143 /**
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.
139146 *
140147 * @param propertyType type to check
141148 * @param value value to check
142149 *
143150 * @return property path
144151 * @throws HibernateException error while getting subcomponent values
145152 */
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 );
149156 }
150- if ( propertyType instanceof ComponentType ) {
151- return checkComponentNullability ( value , ( ComponentType ) propertyType );
157+ else if ( propertyType instanceof ComponentType componentType ) {
158+ return checkComponentNullability ( value , componentType );
152159 }
153-
154- if ( propertyType instanceof CollectionType collectionType ) {
160+ else if ( propertyType instanceof CollectionType collectionType ) {
155161 // 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 ) {
159163 // 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 ();
164167 if ( compositeElement != null ) {
165- return checkComponentNullability ( compositeElement , componentType );
168+ final String path = checkComponentNullability ( compositeElement , componentType );
169+ if ( path != null ) {
170+ return path ;
171+ }
166172 }
167173 }
168174 }
175+ return null ;
176+ }
177+ else {
178+ return null ;
169179 }
170-
171- return null ;
172180 }
173181
174182 /**
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.
177185 *
178- * @param value component properties
186+ * @param composite component properties
179187 * @param compositeType component not-nullable type
180188 *
181189 * @return property path
182190 * @throws HibernateException error while getting subcomponent values
183191 */
184- private String checkComponentNullability (Object value , CompositeType compositeType ) throws HibernateException {
192+ private String checkComponentNullability (Object composite , CompositeType compositeType ) {
185193 // IMPL NOTE : we currently skip checking "any" and "many to any" mappings.
186194 //
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
188196 // 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
190198 // the identifier is not yet generated/assigned (is null)
191199 //
192200 // The more correct fix would be to cascade saves of the many-to-any elements before the Nullability checking
193201
194202 if ( compositeType instanceof AnyType ) {
195203 return null ;
196204 }
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+ }
212223 }
213224 }
214225 }
226+ return null ;
215227 }
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 ;
229228 }
230229
231230}
0 commit comments