2222import static org .hibernate .internal .util .StringHelper .qualify ;
2323
2424/**
25- * Implements the algorithm for validating property values for illegal null values
25+ * Implements the algorithm for validating property values for illegal null values.
26+ * <p>
27+ * For example, a field or property does not accept null values if it is mapped
28+ * {@link jakarta.persistence.Basic#optional @Basic(optional=false)}.
2629 *
2730 * @author Gavin King
2831 */
@@ -81,8 +84,8 @@ public void checkNullability(
8184 */
8285 public void checkNullability (final Object [] values , final EntityPersister persister ) {
8386
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.
87+ // Typically, when Bean Validation is present , we don't validate
88+ // not-null values here . Hence, the checkNullability setting.
8689 if ( checkNullability ) {
8790 // Algorithm:
8891 // Check for any level one nullability breaks
@@ -91,51 +94,59 @@ public void checkNullability(final Object[] values, final EntityPersister persis
9194 // Look at Collections containing components to
9295 // recursively check next level of nullability breaks
9396 //
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
97+ // In the previous implementation, not-null stuff was checked
98+ // filtering by level one only updatable or insertable columns.
99+ // So setting a subcomponent as update="false" has no effect on
100+ // not-null check if the main component had good checkability
98101 // In this implementation, we keep this feature.
99- // However, I never see any documentation mentioning that, but it's for
100- // sure a limitation.
102+ // However, I never see any documentation mentioning that, but
103+ // it's for sure a limitation.
101104
102105 final boolean [] nullability = persister .getPropertyNullability ();
103- final boolean [] checkability = checkType == NullabilityCheckType .CREATE
104- ? persister .getPropertyInsertability ()
105- : persister .getPropertyUpdateability ();
106+ final boolean [] checkability = getCheckability ( persister );
106107 final Type [] propertyTypes = persister .getPropertyTypes ();
107108 final Generator [] generators = persister .getEntityMetamodel ().getGenerators ();
108109 for ( int i = 0 ; i < values .length ; i ++ ) {
109110 if ( checkability [i ]
110- && values [i ] != LazyPropertyInitializer . UNFETCHED_PROPERTY
111+ && ! unfetched ( values [i ] )
111112 && !generated ( generators [i ] ) ) {
112113 final Object value = values [i ];
113- if ( !nullability [i ] && value == null ) {
114- //check basic level one nullability
115- throw new PropertyValueException (
116- "not-null property references a null or transient value" ,
117- persister .getEntityName (),
118- persister .getPropertyNames ()[i ]
114+ if ( value == null ) {
115+ if ( !nullability [i ] ) {
116+ // check basic level-one nullability
117+ throw new PropertyValueException (
118+ "not-null property references a null or transient value" ,
119+ persister .getEntityName (),
120+ persister .getPropertyNames ()[i ]
119121 );
120-
122+ }
121123 }
122- else if ( value != null ) {
123- //values is not null and is checkable, we'll look deeper
124+ else {
125+ // values is not null and is checkable, we'll look deeper
124126 final String breakProperties = checkSubElementsNullability ( propertyTypes [i ], value );
125127 if ( breakProperties != null ) {
126128 throw new PropertyValueException (
127- "not-null property references a null or transient value" ,
128- persister .getEntityName (),
129- qualify ( persister .getPropertyNames ()[i ], breakProperties )
129+ "not-null property references a null or transient value" ,
130+ persister .getEntityName (),
131+ qualify ( persister .getPropertyNames ()[i ], breakProperties )
130132 );
131133 }
132-
133134 }
134135 }
135136 }
136137 }
137138 }
138139
140+ private boolean [] getCheckability (EntityPersister persister ) {
141+ return checkType == NullabilityCheckType .CREATE
142+ ? persister .getPropertyInsertability ()
143+ : persister .getPropertyUpdateability ();
144+ }
145+
146+ private static boolean unfetched (Object value ) {
147+ return value == LazyPropertyInitializer .UNFETCHED_PROPERTY ;
148+ }
149+
139150 private static boolean generated (Generator generator ) {
140151 return generator != null && generator .generatesSometimes ();
141152 }
@@ -190,35 +201,36 @@ else if ( propertyType instanceof CollectionType collectionType ) {
190201 * @throws HibernateException error while getting subcomponent values
191202 */
192203 private String checkComponentNullability (Object composite , CompositeType compositeType ) {
193- // IMPL NOTE : we currently skip checking "any" and "many to any" mappings.
204+ // IMPL NOTE: we currently skip checking "any" and "many-to- any" mappings.
194205 //
195- // This is not the best solution. But atm there is a mismatch between AnyType# getPropertyNullability
196- // and the fact that cascaded-saves for "many to any" mappings are not performed until after this nullability
197- // check. So the nullability check fails for transient entity elements with generated identifiers because
198- // the identifier is not yet generated/assigned (is null)
206+ // This is not the best solution. But there's a mismatch between AnyType. getPropertyNullability()
207+ // and the fact that cascaded-saves for "many-to- any" mappings are not performed until after this
208+ // nullability check. So the nullability check fails for transient entity elements with generated
209+ // identifiers because the identifier is not yet generated/assigned (is null).
199210 //
200- // The more correct fix would be to cascade saves of the many-to-any elements before the Nullability checking
211+ // The fix would be to cascade saves of the many-to-any elements before Nullability checking.
201212
202213 if ( compositeType instanceof AnyType ) {
203214 return null ;
204215 }
205216 else {
206217 final boolean [] nullability = compositeType .getPropertyNullability ();
207218 if ( nullability != null ) {
208- //do the test
219+ // do the test
209220 final Object [] values = compositeType .getPropertyValues ( composite , session );
210221 final Type [] propertyTypes = compositeType .getSubtypes ();
222+ final String [] propertyNames = compositeType .getPropertyNames ();
211223 for ( int i = 0 ; i < values .length ; i ++ ) {
212224 final Object value = values [i ];
213225 if ( value == null ) {
214226 if ( !nullability [i ] ) {
215- return compositeType . getPropertyNames () [i ];
227+ return propertyNames [i ];
216228 }
217229 }
218230 else {
219231 final String breakProperties = checkSubElementsNullability ( propertyTypes [i ], value );
220232 if ( breakProperties != null ) {
221- return qualify ( compositeType . getPropertyNames () [i ], breakProperties );
233+ return qualify ( propertyNames [i ], breakProperties );
222234 }
223235 }
224236 }
0 commit comments