diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/Nullability.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/Nullability.java index 80611bf1a00e..f30c00bf424a 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/Nullability.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/Nullability.java @@ -22,7 +22,10 @@ import static org.hibernate.internal.util.StringHelper.qualify; /** - * Implements the algorithm for validating property values for illegal null values + * Implements the algorithm for validating property values for illegal null values. + *

+ * For example, a field or property does not accept null values if it is mapped + * {@link jakarta.persistence.Basic#optional @Basic(optional=false)}. * * @author Gavin King */ @@ -81,8 +84,8 @@ public void checkNullability( */ public void checkNullability(final Object[] values, final EntityPersister persister) { - // Typically when Bean Validation is on, we don't want to validate null values - // at the Hibernate Core level. Hence, the checkNullability setting. + // Typically, when Bean Validation is present, we don't validate + // not-null values here. Hence, the checkNullability setting. if ( checkNullability ) { // Algorithm: // Check for any level one nullability breaks @@ -91,51 +94,59 @@ public void checkNullability(final Object[] values, final EntityPersister persis // Look at Collections containing components to // recursively check next level of nullability breaks // - // In the previous implementation, not-null stuffs where checked - // filtering by level one only updatable - // or insertable columns. So setting a subcomponent as update="false" - // has no effect on not-null check if the main component had good checkability + // In the previous implementation, not-null stuff was checked + // filtering by level one only updatable or insertable columns. + // So setting a subcomponent as update="false" has no effect on + // not-null check if the main component had good checkability // In this implementation, we keep this feature. - // However, I never see any documentation mentioning that, but it's for - // sure a limitation. + // However, I never see any documentation mentioning that, but + // it's for sure a limitation. final boolean[] nullability = persister.getPropertyNullability(); - final boolean[] checkability = checkType == NullabilityCheckType.CREATE - ? persister.getPropertyInsertability() - : persister.getPropertyUpdateability(); + final boolean[] checkability = getCheckability( persister ); final Type[] propertyTypes = persister.getPropertyTypes(); final Generator[] generators = persister.getEntityMetamodel().getGenerators(); for ( int i = 0; i < values.length; i++ ) { if ( checkability[i] - && values[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY + && !unfetched( values[i] ) && !generated( generators[i] ) ) { final Object value = values[i]; - if ( !nullability[i] && value == null ) { - //check basic level one nullability - throw new PropertyValueException( - "not-null property references a null or transient value", - persister.getEntityName(), - persister.getPropertyNames()[i] + if ( value == null ) { + if ( !nullability[i] ) { + // check basic level-one nullability + throw new PropertyValueException( + "not-null property references a null or transient value", + persister.getEntityName(), + persister.getPropertyNames()[i] ); - + } } - else if ( value != null ) { - //values is not null and is checkable, we'll look deeper + else { + // values is not null and is checkable, we'll look deeper final String breakProperties = checkSubElementsNullability( propertyTypes[i], value ); if ( breakProperties != null ) { throw new PropertyValueException( - "not-null property references a null or transient value", - persister.getEntityName(), - qualify( persister.getPropertyNames()[i], breakProperties ) + "not-null property references a null or transient value", + persister.getEntityName(), + qualify( persister.getPropertyNames()[i], breakProperties ) ); } - } } } } } + private boolean[] getCheckability(EntityPersister persister) { + return checkType == NullabilityCheckType.CREATE + ? persister.getPropertyInsertability() + : persister.getPropertyUpdateability(); + } + + private static boolean unfetched(Object value) { + return value == LazyPropertyInitializer.UNFETCHED_PROPERTY; + } + private static boolean generated(Generator generator) { return generator != null && generator.generatesSometimes(); } @@ -190,14 +201,14 @@ else if ( propertyType instanceof CollectionType collectionType ) { * @throws HibernateException error while getting subcomponent values */ private String checkComponentNullability(Object composite, CompositeType compositeType) { - // IMPL NOTE : we currently skip checking "any" and "many to any" mappings. + // IMPL NOTE: we currently skip checking "any" and "many-to-any" mappings. // - // This is not the best solution. But atm there is a mismatch between AnyType#getPropertyNullability - // and the fact that cascaded-saves for "many to any" mappings are not performed until after this nullability - // check. So the nullability check fails for transient entity elements with generated identifiers because - // the identifier is not yet generated/assigned (is null) + // This is not the best solution. But there's a mismatch between AnyType.getPropertyNullability() + // and the fact that cascaded-saves for "many-to-any" mappings are not performed until after this + // nullability check. So the nullability check fails for transient entity elements with generated + // identifiers because the identifier is not yet generated/assigned (is null). // - // The more correct fix would be to cascade saves of the many-to-any elements before the Nullability checking + // The fix would be to cascade saves of the many-to-any elements before Nullability checking. if ( compositeType instanceof AnyType ) { return null; @@ -205,20 +216,21 @@ private String checkComponentNullability(Object composite, CompositeType composi else { final boolean[] nullability = compositeType.getPropertyNullability(); if ( nullability != null ) { - //do the test + // do the test final Object[] values = compositeType.getPropertyValues( composite, session ); final Type[] propertyTypes = compositeType.getSubtypes(); + final String[] propertyNames = compositeType.getPropertyNames(); for ( int i = 0; i < values.length; i++ ) { final Object value = values[i]; if ( value == null ) { if ( !nullability[i] ) { - return compositeType.getPropertyNames()[i]; + return propertyNames[i]; } } else { final String breakProperties = checkSubElementsNullability( propertyTypes[i], value ); if ( breakProperties != null ) { - return qualify( compositeType.getPropertyNames()[i], breakProperties ); + return qualify( propertyNames[i], breakProperties ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/QueryProducer.java b/hibernate-core/src/main/java/org/hibernate/query/QueryProducer.java index 4e03748d7ab0..71f3f2870632 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/QueryProducer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/QueryProducer.java @@ -191,8 +191,6 @@ public interface QueryProducer { * @return The {@link NativeQuery} instance for manipulation and execution * * @see jakarta.persistence.EntityManager#createNativeQuery(String,Class) - * - * @apiNote Changes in JPA 3.2 required de-typing this to be compilable with their changes */ NativeQuery createNativeQuery(String sqlString, Class resultClass);