From 7ff76bf6ebf920fe4326abb324c3aab0e0c4f033 Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Tue, 8 Aug 2023 23:46:35 +0200 Subject: [PATCH] HHH-16515 - Add o.h.engine.spi to nullness checking Signed-off-by: Jan Schatteman --- checkerstubs/java.lang.astub | 15 +++- checkerstubs/jboss.logging.astub | 4 ++ gradle/java-module.gradle | 2 +- .../collection/spi/PersistentCollection.java | 4 +- .../query/spi/EntityGraphQueryHint.java | 6 +- .../org/hibernate/engine/spi/ActionQueue.java | 29 ++++---- .../hibernate/engine/spi/AssociationKey.java | 4 +- .../hibernate/engine/spi/BatchFetchQueue.java | 67 +++++++++-------- .../hibernate/engine/spi/CollectionEntry.java | 71 ++++++++++--------- .../hibernate/engine/spi/CollectionKey.java | 9 ++- .../engine/spi/EffectiveEntityGraph.java | 16 +++-- .../org/hibernate/engine/spi/EntityKey.java | 6 +- .../hibernate/engine/spi/EntityUniqueKey.java | 13 +++- .../hibernate/engine/spi/ExecutableList.java | 13 ++-- .../spi/ExecuteUpdateResultCheckStyle.java | 8 ++- .../engine/spi/FilterDefinition.java | 10 ++- .../hibernate/engine/spi/IdentifierValue.java | 16 +++-- .../engine/spi/LoadQueryInfluencers.java | 21 +++--- .../spi/PrimeAmongSecondarySupertypes.java | 22 +++--- .../engine/spi/SessionDelegatorBaseImpl.java | 11 +-- .../engine/spi/SessionLazyDelegator.java | 9 +-- .../spi/SharedSessionContractImplementor.java | 3 +- .../spi/SharedSessionDelegatorBaseImpl.java | 4 +- .../org/hibernate/engine/spi/TypedValue.java | 12 ++-- .../engine/spi/UnsavedValueStrategy.java | 6 +- .../hibernate/engine/spi/VersionValue.java | 16 +++-- .../org/hibernate/graph/spi/AppliedGraph.java | 6 +- .../hibernate/internal/util/StringHelper.java | 4 +- .../metamodel/mapping/ModelPart.java | 4 +- .../query/internal/QueryOptionsImpl.java | 5 +- 30 files changed, 252 insertions(+), 164 deletions(-) diff --git a/checkerstubs/java.lang.astub b/checkerstubs/java.lang.astub index f6a2418284..23c7a1d895 100644 --- a/checkerstubs/java.lang.astub +++ b/checkerstubs/java.lang.astub @@ -8,7 +8,18 @@ public final class Method extends Executable { void invoke(@Nullable Object obj, @Nullable Object @Nullable ... args); } -public final -class Field extends AccessibleObject implements Member { +public final class Field extends AccessibleObject implements Member { public void set(Object obj, @Nullable Object value); } + +package java.util; + + +public interface Queue extends Collection { + @Override + @EnsuresNonNullIf(expression="poll()", result=false) + @EnsuresNonNullIf(expression="peek()", result=false) + boolean isEmpty(); + E poll(); + E peek(); +} diff --git a/checkerstubs/jboss.logging.astub b/checkerstubs/jboss.logging.astub index 994df8bf87..d3bc6a0626 100644 --- a/checkerstubs/jboss.logging.astub +++ b/checkerstubs/jboss.logging.astub @@ -6,4 +6,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; public interface BasicLogger { void tracef(String format, @Nullable Object param1, @Nullable Object param2); +} + +public interface Logger { + void tracev(String format, @Nullable Object param1); } \ No newline at end of file diff --git a/gradle/java-module.gradle b/gradle/java-module.gradle index 9bc6054223..ee7d348385 100644 --- a/gradle/java-module.gradle +++ b/gradle/java-module.gradle @@ -487,7 +487,7 @@ checkerFramework { extraJavacArgs = [ '-AsuppressWarnings=initialization', // stubs is passed directly through options.compilerArgumentProviders - '-AonlyDefs=^org\\.hibernate\\.(jdbc|exception|integrator|jpamodelgen|service|spi|pretty|property\\.access|stat|engine\\.(config|jndi|profile|transaction)|(action|context|bytecode)\\.spi)\\.' + '-AonlyDefs=^org\\.hibernate\\.(jdbc|exception|integrator|jpamodelgen|service|spi|pretty|property\\.access|stat|engine\\.(config|jndi|profile|spi|transaction)|(action|context|bytecode)\\.spi)\\.' ] } diff --git a/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java b/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java index e76e007ffa..2189b1d7ff 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java @@ -19,6 +19,8 @@ import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.mutation.InsertRowsCoordinator; import org.hibernate.type.Type; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Persistent collections are treated as value objects by Hibernate. * They have no independent existence beyond the entity holding a @@ -82,7 +84,7 @@ public interface PersistentCollection extends LazyInitializable { * @param role The collection role * @param snapshot The snapshot state */ - void setSnapshot(Object key, String role, Serializable snapshot); + void setSnapshot(@Nullable Object key, @Nullable String role, @Nullable Serializable snapshot); /** * After flushing, clear any "queued" additions, since the diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/EntityGraphQueryHint.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/EntityGraphQueryHint.java index 279a40b790..8e330cd979 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/EntityGraphQueryHint.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/EntityGraphQueryHint.java @@ -10,6 +10,8 @@ import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.AppliedGraph; import org.hibernate.graph.spi.RootGraphImplementor; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Encapsulates a JPA EntityGraph provided through a JPQL query hint. Converts the fetches into a list of AST * FromElements. The logic is kept here as much as possible in order to make it easy to remove this in the future, @@ -28,12 +30,12 @@ public class EntityGraphQueryHint implements AppliedGraph { } @Override - public GraphSemantic getSemantic() { + public @Nullable GraphSemantic getSemantic() { return delegate.getSemantic(); } @Override - public RootGraphImplementor getGraph() { + public @Nullable RootGraphImplementor getGraph() { return delegate.getGraph(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java index 65a118ecbe..345b8d4b6f 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java @@ -46,7 +46,6 @@ import org.hibernate.engine.internal.NonNullableTransientDependencies; import org.hibernate.event.spi.EventSource; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; -import org.hibernate.internal.util.StringHelper; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.internal.EntityCollectionPart; import org.hibernate.persister.entity.EntityPersister; @@ -57,6 +56,8 @@ import org.hibernate.type.EntityType; import org.hibernate.type.ForeignKeyDirection; import org.hibernate.type.Type; +import org.checkerframework.checker.nullness.qual.Nullable; + import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; /** @@ -511,7 +512,7 @@ public class ActionQueue { prepareActions( collectionQueuedOps ); } - private void prepareActions(ExecutableList queue) throws HibernateException { + private void prepareActions(@Nullable ExecutableList queue) throws HibernateException { if ( queue == null ) { return; } @@ -583,7 +584,7 @@ public class ActionQueue { return areTablesToBeUpdated( unresolvedInsertions, tables ); } - private static boolean areTablesToBeUpdated(ExecutableList actions, Set tableSpaces) { + private static boolean areTablesToBeUpdated(@Nullable ExecutableList actions, Set tableSpaces) { if ( actions == null || actions.isEmpty() ) { return false; } @@ -617,7 +618,7 @@ public class ActionQueue { * @param list The list of Executable elements to be performed * */ - private void executeActions(ExecutableList list) + private void executeActions(@Nullable ExecutableList list) throws HibernateException { if ( list == null || list.isEmpty() ) { return; @@ -651,8 +652,10 @@ public class ActionQueue { // Strictly speaking, only a subset of the list may have been processed if a RuntimeException occurs. // We still invalidate all spaces. I don't see this as a big deal - after all, RuntimeExceptions are // unexpected. - invalidateSpaces( list.getQuerySpaces().toArray(StringHelper.EMPTY_STRINGS) ); + invalidateSpaces( list.getQuerySpaces().toArray(new String[0]) ); } + // @NonNull String @Nullable [] - array nullable, elements not + // @Nullable String @NonNull [] - elements nullable, array not } list.clear(); @@ -676,7 +679,7 @@ public class ActionQueue { * * @param spaces The spaces to invalidate */ - private void invalidateSpaces(String[] spaces) { + private void invalidateSpaces(String @Nullable [] spaces) { if ( spaces != null && spaces.length > 0 ) { for ( String space : spaces ) { if ( afterTransactionProcesses == null ) { @@ -708,7 +711,7 @@ public class ActionQueue { + "]"; } - private static String toString(ExecutableList q) { + private static String toString(@Nullable ExecutableList q) { return q == null ? "ExecutableList{size=0}" : q.toString(); } @@ -858,7 +861,7 @@ public class ActionQueue { || nonempty( collectionCreations ); } - private boolean nonempty(ExecutableList list) { + private boolean nonempty(@Nullable ExecutableList list) { return list != null && !list.isEmpty(); } @@ -984,7 +987,7 @@ public class ActionQueue { this.session = session; } - public void register(T process) { + public void register(@Nullable T process) { if ( process != null ) { processes.add( process ); } @@ -1051,7 +1054,7 @@ public class ActionQueue { if ( session.getFactory().getSessionFactoryOptions().isQueryCacheEnabled() ) { session.getFactory().getCache().getTimestampsCache().invalidate( - querySpacesToInvalidate.toArray(StringHelper.EMPTY_STRINGS), + querySpacesToInvalidate.toArray(new String[0]), session ); } @@ -1149,7 +1152,7 @@ public class ActionQueue { } } - private void addDirectDependency(Type type, Object value, IdentityHashMap insertInfosByEntity) { + private void addDirectDependency(Type type, @Nullable Object value, IdentityHashMap insertInfosByEntity) { if ( type.isEntityType() && value != null ) { final EntityType entityType = (EntityType) type; final InsertInfo insertInfo = insertInfosByEntity.get( value ); @@ -1209,7 +1212,7 @@ public class ActionQueue { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if ( this == o ) { return true; } @@ -1305,7 +1308,7 @@ public class ActionQueue { } } - private int schedule(InsertInfo[] insertInfos, List insertInfosToSchedule, int schedulePosition) { + private int schedule(InsertInfo [] insertInfos, List insertInfosToSchedule, int schedulePosition) { final InsertInfo[] newInsertInfos = new InsertInfo[insertInfos.length]; // The bitset is there to quickly query if an index is already scheduled final BitSet bitSet = new BitSet(insertInfos.length); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/AssociationKey.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/AssociationKey.java index 81404a74ab..d9966d38ca 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/AssociationKey.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/AssociationKey.java @@ -8,6 +8,8 @@ package org.hibernate.engine.spi; import java.io.Serializable; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Identifies a named association belonging to a particular * entity instance. Used to record the fact that an association @@ -31,7 +33,7 @@ public final class AssociationKey implements Serializable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if ( this == o ) { return true; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java index 352cde07ca..7aa6a6c48d 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java @@ -26,6 +26,9 @@ import org.hibernate.persister.entity.EntityPersister; import org.jboss.logging.Logger; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Keeps track of:
    *
  • entity and collection keys that are available for batch fetching
  • @@ -45,22 +48,22 @@ public class BatchFetchQueue { * A map of {@link SubselectFetch subselect-fetch descriptors} keyed by the * {@link EntityKey} against which the descriptor is registered. */ - private Map subselectsByEntityKey; + private @Nullable Map subselectsByEntityKey; /** - * Used to hold information about the entities that are currently eligible for batch-fetching. Ultimately + * Used to hold information about the entities that are currently eligible for batch-fetching. Ultimately * used by {@link #getBatchLoadableEntityIds} to build entity load batches. *

    * A Map structure is used to segment the keys by entity type since loading can only be done for a particular entity * type at a time. */ - private Map > batchLoadableEntityKeys; + private @Nullable Map > batchLoadableEntityKeys; /** - * Used to hold information about the collections that are currently eligible for batch-fetching. Ultimately + * Used to hold information about the collections that are currently eligible for batch-fetching. Ultimately * used by {@link #getCollectionBatch} to build collection load batches. */ - private Map>> batchLoadableCollections; + private @Nullable Map>> batchLoadableCollections; /** * Constructs a queue for the given context. @@ -92,7 +95,7 @@ public class BatchFetchQueue { * @return The fetch descriptor; may return null if no subselect fetch queued for * this entity key. */ - public SubselectFetch getSubselect(EntityKey key) { + public @Nullable SubselectFetch getSubselect(EntityKey key) { if ( subselectsByEntityKey == null ) { return null; } @@ -165,9 +168,9 @@ public class BatchFetchQueue { * if necessary */ public void removeBatchLoadableEntityKey(EntityKey key) { - if ( batchLoadableEntityKeys != null - && key.isBatchLoadable( context.getSession().getLoadQueryInfluencers() ) ) { - final LinkedHashSet set = batchLoadableEntityKeys.get( key.getEntityName() ); + if ( key.isBatchLoadable( context.getSession().getLoadQueryInfluencers() ) + && batchLoadableEntityKeys != null ) { + final LinkedHashSet set = batchLoadableEntityKeys.get( key.getEntityName() ); if ( set != null ) { set.remove( key ); } @@ -175,12 +178,11 @@ public class BatchFetchQueue { } /** - * Intended for test usage. Really has no use-case in Hibernate proper. + * Intended for test usage. Really has no use-case in Hibernate proper. */ public boolean containsEntityKey(EntityKey key) { - if ( batchLoadableEntityKeys != null - && key.isBatchLoadable( context.getSession().getLoadQueryInfluencers() ) ) { - LinkedHashSet set = batchLoadableEntityKeys.get( key.getEntityName() ); + if ( key.isBatchLoadable( context.getSession().getLoadQueryInfluencers() ) && batchLoadableEntityKeys != null ) { + LinkedHashSet set = batchLoadableEntityKeys.get( key.getEntityName() ); if ( set != null ) { return set.contains( key ); } @@ -189,7 +191,7 @@ public class BatchFetchQueue { } /** - * A "collector" form of {@link #getBatchLoadableEntityIds}. Useful + * A "collector" form of {@link #getBatchLoadableEntityIds}. Useful * in cases where we want a specially created array/container - allows * creation of concretely typed array for ARRAY param binding to ensure * the driver does not need to cast/copy the values array. @@ -197,7 +199,7 @@ public class BatchFetchQueue { public void collectBatchLoadableEntityIds( final int domainBatchSize, IndexedConsumer collector, - final T loadingId, + final @NonNull T loadingId, final EntityMappingType entityDescriptor) { // make sure we load the id being loaded in the batch! collector.accept( 0, loadingId ); @@ -206,7 +208,7 @@ public class BatchFetchQueue { return; } - final LinkedHashSet set = batchLoadableEntityKeys.get( entityDescriptor.getEntityName() ); + final LinkedHashSet set = batchLoadableEntityKeys.get( entityDescriptor.getEntityName() ); if ( set == null ) { return; } @@ -248,7 +250,7 @@ public class BatchFetchQueue { * complex algorithm that tries to grab keys registered immediately after * the given key. */ - public Object[] getBatchLoadableEntityIds( + public Object [] getBatchLoadableEntityIds( final EntityMappingType entityDescriptor, final Object loadingId, final int maxBatchSize) { @@ -328,6 +330,7 @@ public class BatchFetchQueue { batchLoadableCollections = CollectionHelper.mapOfSize( 12 ); } + assert persister != null : "@AssumeAssertion(nullness)"; final LinkedHashMap> map = batchLoadableCollections.computeIfAbsent( persister.getRole(), @@ -343,11 +346,13 @@ public class BatchFetchQueue { * if necessary */ public void removeBatchLoadableCollection(CollectionEntry ce) { + final CollectionPersister persister = ce.getLoadedPersister(); if ( batchLoadableCollections == null ) { return; } + assert persister != null : "@AssumeAssertion(nullness)"; LinkedHashMap> map = - batchLoadableCollections.get( ce.getLoadedPersister().getRole() ); + batchLoadableCollections.get( persister.getRole() ); if ( map != null ) { map.remove( ce ); } @@ -355,7 +360,7 @@ public class BatchFetchQueue { /** - * A "collector" form of {@link #getCollectionBatch}. Useful + * A "collector" form of {@link #getCollectionBatch}. Useful * in cases where we want a specially created array/container - allows * creation of concretely typed array for ARRAY param binding to ensure * the driver does not need to cast/copy the values array. @@ -363,7 +368,7 @@ public class BatchFetchQueue { public void collectBatchLoadableCollectionKeys( int batchSize, IndexedConsumer collector, - T keyBeingLoaded, + @NonNull T keyBeingLoaded, PluralAttributeMapping pluralAttributeMapping) { collector.accept( 0, keyBeingLoaded ); @@ -383,9 +388,10 @@ public class BatchFetchQueue { for ( Entry> me : map.entrySet() ) { final CollectionEntry ce = me.getKey(); + final Object loadedKey = ce.getLoadedKey(); final PersistentCollection collection = me.getValue(); - if ( ce.getLoadedKey() == null ) { + if ( loadedKey == null ) { // the loadedKey of the collectionEntry might be null as it might have been reset to null // (see for example Collections.processDereferencedCollection() // and CollectionEntry.afterAction()) @@ -407,21 +413,21 @@ public class BatchFetchQueue { final boolean isEqual = pluralAttributeMapping.getKeyDescriptor().areEqual( keyBeingLoaded, - ce.getLoadedKey(), + loadedKey, context.getSession() ); // final boolean isEqual = collectionPersister.getKeyType().isEqual( // id, -// ce.getLoadedKey(), +// loadedKey, // collectionPersister.getFactory() // ); if ( isEqual ) { end = i; } - else if ( !isCached( ce.getLoadedKey(), pluralAttributeMapping.getCollectionDescriptor() ) ) { + else if ( !isCached( loadedKey, pluralAttributeMapping.getCollectionDescriptor() ) ) { //noinspection unchecked - collector.accept( i++, (T) ce.getLoadedKey() ); + collector.accept( i++, (T) loadedKey ); } if ( i == batchSize ) { @@ -444,7 +450,7 @@ public class BatchFetchQueue { * @param batchSize the maximum number of keys to return * @return an array of collection keys, of length batchSize (padded with nulls) */ - public Object[] getCollectionBatch( + public Object [] getCollectionBatch( final CollectionPersister collectionPersister, final Object id, final int batchSize) { @@ -465,9 +471,10 @@ public class BatchFetchQueue { if ( map != null ) { for ( Entry> me : map.entrySet() ) { final CollectionEntry ce = me.getKey(); + final Object loadedKey = ce.getLoadedKey(); final PersistentCollection collection = me.getValue(); - if ( ce.getLoadedKey() == null ) { + if ( loadedKey == null ) { // the loadedKey of the collectionEntry might be null as it might have been reset to null // (see for example Collections.processDereferencedCollection() // and CollectionEntry.afterAction()) @@ -488,7 +495,7 @@ public class BatchFetchQueue { final boolean isEqual = collectionPersister.getKeyType().isEqual( id, - ce.getLoadedKey(), + loadedKey, collectionPersister.getFactory() ); @@ -496,8 +503,8 @@ public class BatchFetchQueue { end = i; //checkForEnd = false; } - else if ( !isCached( ce.getLoadedKey(), collectionPersister ) ) { - keys[i++] = ce.getLoadedKey(); + else if ( !isCached( loadedKey, collectionPersister ) ) { + keys[i++] = loadedKey; //count++; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionEntry.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionEntry.java index dec5fc7f07..b9b65886f3 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionEntry.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionEntry.java @@ -12,6 +12,7 @@ import org.hibernate.collection.spi.AbstractPersistentCollection; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.internal.util.NullnessUtil; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.pretty.MessageHelper; @@ -21,6 +22,9 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Collection; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * We need an entry to tell us all about the current state * of a collection with respect to its persistent state @@ -33,14 +37,14 @@ public final class CollectionEntry implements Serializable { //ATTRIBUTES MAINTAINED BETWEEN FLUSH CYCLES // session-start/post-flush persistent state - private Serializable snapshot; + private @Nullable Serializable snapshot; // allow the CollectionSnapshot to be serialized - private String role; + private @Nullable String role; // "loaded" means the reference that is consistent // with the current database state - private transient CollectionPersister loadedPersister; - private Object loadedKey; + private transient @Nullable CollectionPersister loadedPersister; + private @Nullable Object loadedKey; // ATTRIBUTES USED ONLY DURING FLUSH CYCLE @@ -56,8 +60,8 @@ public final class CollectionEntry implements Serializable { private transient boolean ignore; // "current" means the reference that was found during flush() - private transient CollectionPersister currentPersister; - private transient Object currentKey; + private transient @Nullable CollectionPersister currentPersister; + private transient @Nullable Object currentKey; /** * For newly wrapped collections, or dereferenced collection wrappers @@ -88,7 +92,9 @@ public final class CollectionEntry implements Serializable { //collection.clearDirty() this.loadedKey = loadedKey; - setLoadedPersister( loadedPersister ); + + this.loadedPersister = loadedPersister; + this.role = ( loadedPersister == null ? null : loadedPersister.getRole() ); collection.setSnapshot( loadedKey, role, null ); @@ -106,7 +112,8 @@ public final class CollectionEntry implements Serializable { //collection.clearDirty() this.loadedKey = loadedKey; - setLoadedPersister( loadedPersister ); + this.loadedPersister = loadedPersister; + this.role = ( loadedPersister == null ? null : loadedPersister.getRole() ); } /** @@ -118,9 +125,8 @@ public final class CollectionEntry implements Serializable { ignore = false; loadedKey = collection.getKey(); - setLoadedPersister( - factory.getRuntimeMetamodels().getMappingMetamodel().getCollectionDescriptor( collection.getRole() ) - ); + loadedPersister = factory.getRuntimeMetamodels().getMappingMetamodel().getCollectionDescriptor( collection.getRole() ); + this.role = ( loadedPersister == null ? null : loadedPersister.getRole() ); snapshot = collection.getStoredSnapshot(); } @@ -132,10 +138,10 @@ public final class CollectionEntry implements Serializable { * @see #deserialize */ private CollectionEntry( - String role, + @Nullable String role, Serializable snapshot, Object loadedKey, - SessionFactoryImplementor factory) { + @Nullable SessionFactoryImplementor factory) { this.role = role; this.snapshot = snapshot; this.loadedKey = loadedKey; @@ -176,7 +182,7 @@ public final class CollectionEntry implements Serializable { if ( nonMutableChange ) { throw new HibernateException( "changed an immutable collection instance: " + - MessageHelper.collectionInfoString( loadedPersister.getRole(), getLoadedKey() ) + MessageHelper.collectionInfoString( NullnessUtil.castNonNull( loadedPersister ).getRole(), getLoadedKey() ) ); } @@ -199,12 +205,12 @@ public final class CollectionEntry implements Serializable { public void postInitialize(PersistentCollection collection) throws HibernateException { final CollectionPersister loadedPersister = getLoadedPersister(); - snapshot = loadedPersister.isMutable() + snapshot = loadedPersister != null && loadedPersister.isMutable() ? collection.getSnapshot( loadedPersister ) : null; collection.setSnapshot( loadedKey, role, snapshot ); final SharedSessionContractImplementor session = ((AbstractPersistentCollection) collection).getSession(); - if ( session.getLoadQueryInfluencers().effectivelyBatchLoadable( loadedPersister ) ) { + if ( loadedPersister != null && session.getLoadQueryInfluencers().effectivelyBatchLoadable( loadedPersister ) ) { session.getPersistenceContextInternal() .getBatchFetchQueue() .removeBatchLoadableCollection( this ); @@ -236,21 +242,21 @@ public final class CollectionEntry implements Serializable { if ( resnapshot ) { snapshot = loadedPersister == null || !loadedPersister.isMutable() ? null : - collection.getSnapshot( loadedPersister ); //re-snapshot + collection.getSnapshot( NullnessUtil.castNonNull( loadedPersister ) ); //re-snapshot } collection.postAction(); } - public Object getKey() { + public @Nullable Object getKey() { return getLoadedKey(); } - public String getRole() { + public @Nullable String getRole() { return role; } - public Serializable getSnapshot() { + public @Nullable Serializable getSnapshot() { return snapshot; } @@ -275,17 +281,17 @@ public final class CollectionEntry implements Serializable { fromMerge = true; } - private void setLoadedPersister(CollectionPersister persister) { + private void setLoadedPersister(@Nullable CollectionPersister persister) { loadedPersister = persister; setRole( persister == null ? null : persister.getRole() ); } - void afterDeserialize(SessionFactoryImplementor factory) { + void afterDeserialize(@Nullable SessionFactoryImplementor factory) { if ( factory == null ) { loadedPersister = null; } else { - loadedPersister = factory.getRuntimeMetamodels().getMappingMetamodel().getCollectionDescriptor( role ); + loadedPersister = factory.getRuntimeMetamodels().getMappingMetamodel().getCollectionDescriptor( NullnessUtil.castNonNull( role ) ); } } @@ -337,11 +343,11 @@ public final class CollectionEntry implements Serializable { return ignore; } - public CollectionPersister getCurrentPersister() { + public @Nullable CollectionPersister getCurrentPersister() { return currentPersister; } - public void setCurrentPersister(CollectionPersister currentPersister) { + public void setCurrentPersister(@Nullable CollectionPersister currentPersister) { this.currentPersister = currentPersister; } @@ -349,26 +355,26 @@ public final class CollectionEntry implements Serializable { * This is only available late during the flush * cycle */ - public Object getCurrentKey() { + public @Nullable Object getCurrentKey() { return currentKey; } - public void setCurrentKey(Object currentKey) { + public void setCurrentKey(@Nullable Object currentKey) { this.currentKey = currentKey; } /** * This is only available late during the flush cycle */ - public CollectionPersister getLoadedPersister() { + public @Nullable CollectionPersister getLoadedPersister() { return loadedPersister; } - public Object getLoadedKey() { + public @Nullable Object getLoadedKey() { return loadedKey; } - public void setRole(String role) { + public void setRole(@Nullable String role) { this.role = role; } @@ -398,9 +404,10 @@ public final class CollectionEntry implements Serializable { // does the collection already have // it's own up-to-date snapshot? final CollectionPersister loadedPersister = getLoadedPersister(); + Serializable snapshot = getSnapshot(); return collection.wasInitialized() && - ( loadedPersister ==null || loadedPersister.isMutable() ) && - collection.isSnapshotEmpty( getSnapshot() ); + ( loadedPersister == null || loadedPersister.isMutable() ) && + (snapshot != null ? collection.isSnapshotEmpty( snapshot ) : true); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionKey.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionKey.java index 48431082ee..b06fe36cad 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionKey.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionKey.java @@ -16,6 +16,8 @@ import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.pretty.MessageHelper; import org.hibernate.type.Type; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Uniquely identifies a collection instance in a particular session. * @@ -39,7 +41,7 @@ public final class CollectionKey implements Serializable { private CollectionKey( String role, - Object key, + @Nullable Object key, Type keyType, SessionFactoryImplementor factory) { this.role = role; @@ -77,7 +79,7 @@ public final class CollectionKey implements Serializable { } @Override - public boolean equals(final Object other) { + public boolean equals(final @Nullable Object other) { if ( this == other ) { return true; } @@ -127,7 +129,8 @@ public final class CollectionKey implements Serializable { (String) ois.readObject(), ois.readObject(), (Type) ois.readObject(), - (session == null ? null : session.getFactory()) + // Should never be able to be null + session.getFactory() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/EffectiveEntityGraph.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/EffectiveEntityGraph.java index 1e8a1af40d..660b3913c8 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/EffectiveEntityGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/EffectiveEntityGraph.java @@ -13,9 +13,12 @@ import org.hibernate.Incubating; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.AppliedGraph; import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.internal.util.NullnessUtil; import org.jboss.logging.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Think of this as the composite modeling of a graph * and the semantic. @@ -33,8 +36,8 @@ public class EffectiveEntityGraph implements AppliedGraph, Serializable { private final boolean allowOverwrite; - private GraphSemantic semantic; - private RootGraphImplementor graph; + private @Nullable GraphSemantic semantic; + private @Nullable RootGraphImplementor graph; /** * @implSpec I explicitly made this constructor package protected @@ -59,12 +62,12 @@ public class EffectiveEntityGraph implements AppliedGraph, Serializable { } @Override - public GraphSemantic getSemantic() { + public @Nullable GraphSemantic getSemantic() { return semantic; } @Override - public RootGraphImplementor getGraph() { + public @Nullable RootGraphImplementor getGraph() { return graph; } @@ -75,7 +78,7 @@ public class EffectiveEntityGraph implements AppliedGraph, Serializable { * @throws IllegalArgumentException Thrown if the semantic is null * @throws IllegalStateException If previous state is still available (hasn't been cleared). */ - public void applyGraph(RootGraphImplementor graph, GraphSemantic semantic) { + public void applyGraph(RootGraphImplementor graph, @Nullable GraphSemantic semantic) { if ( semantic == null ) { throw new IllegalArgumentException( "Graph semantic cannot be null" ); } @@ -107,7 +110,7 @@ public class EffectiveEntityGraph implements AppliedGraph, Serializable { * @throws IllegalArgumentException If both kinds of graphs were present in the properties/hints * @throws IllegalStateException If previous state is still available (hasn't been cleared). */ - public void applyConfiguredGraph(Map properties) { + public void applyConfiguredGraph(@Nullable Map properties) { if ( properties == null || properties.isEmpty() ) { return; } @@ -138,6 +141,7 @@ public class EffectiveEntityGraph implements AppliedGraph, Serializable { applyGraph( fetchHint, GraphSemantic.FETCH ); } else { + assert loadHint != null : "@AssumeAssertion(nullness)"; applyGraph( loadHint, GraphSemantic.LOAD ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/EntityKey.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/EntityKey.java index 7026ba9bf9..0e235afa73 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/EntityKey.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/EntityKey.java @@ -15,6 +15,8 @@ import org.hibernate.AssertionFailure; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.pretty.MessageHelper; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Uniquely identifies of an entity instance in a particular Session by identifier. * Note that it's only safe to be used within the scope of a Session: it doesn't consider for example the tenantId @@ -44,7 +46,7 @@ public final class EntityKey implements Serializable { * @param id The entity id * @param persister The entity persister */ - public EntityKey(Object id, EntityPersister persister) { + public EntityKey(@Nullable Object id, EntityPersister persister) { this.persister = persister; if ( id == null ) { throw new AssertionFailure( "null identifier (" + persister.getEntityName() + ")" ); @@ -82,7 +84,7 @@ public final class EntityKey implements Serializable { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if ( this == other ) { return true; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/EntityUniqueKey.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/EntityUniqueKey.java index 0cf91d7352..475140acc8 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/EntityUniqueKey.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/EntityUniqueKey.java @@ -14,6 +14,10 @@ import java.io.Serializable; import org.hibernate.pretty.MessageHelper; import org.hibernate.type.Type; +import org.checkerframework.checker.initialization.qual.UnderInitialization; +import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Used to uniquely key an entity instance in relation to a particular session * by some unique property reference, as opposed to identifier. @@ -41,7 +45,7 @@ public class EntityUniqueKey implements Serializable { this.entityName = entityName; this.key = key; this.keyType = keyType; - this.hashCode = generateHashCode( factory ); + this.hashCode = generateHashCode( entityName, uniqueKeyName, keyType, key, factory ); } public String getEntityName() { @@ -56,7 +60,7 @@ public class EntityUniqueKey implements Serializable { return uniqueKeyName; } - public int generateHashCode(SessionFactoryImplementor factory) { + public static int generateHashCode(String entityName, String uniqueKeyName, Type keyType, Object key, SessionFactoryImplementor factory) { int result = 17; result = 37 * result + entityName.hashCode(); result = 37 * result + uniqueKeyName.hashCode(); @@ -70,7 +74,10 @@ public class EntityUniqueKey implements Serializable { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { + if ( other == null || other.getClass() != EntityUniqueKey.class ) { + return false; + } EntityUniqueKey that = (EntityUniqueKey) other; return that != null && that.entityName.equals( entityName ) && that.uniqueKeyName.equals( uniqueKeyName ) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/ExecutableList.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/ExecutableList.java index a59f52a4d1..2a81126739 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/ExecutableList.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/ExecutableList.java @@ -22,6 +22,8 @@ import org.hibernate.action.spi.Executable; import org.hibernate.event.spi.EventSource; import org.hibernate.internal.util.collections.CollectionHelper; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A list of {@link Executable executeble actions}. Responsible for * {@linkplain #sort() sorting} the executables, and calculating the @@ -47,7 +49,7 @@ public class ExecutableList private final ArrayList executables; - private final Sorter sorter; + private final @Nullable Sorter sorter; private final boolean requiresSorting; private boolean sorted; @@ -57,7 +59,7 @@ public class ExecutableList * invalidate cache regions as it is exposed from {@link #getQuerySpaces}. This value * being {@code null} indicates that the query spaces should be calculated. */ - private transient Set querySpaces; + private transient @Nullable Set querySpaces; /** * Creates a new instance with the default settings. @@ -296,9 +298,10 @@ public class ExecutableList oos.writeInt( -1 ); } else { + final Set qs = querySpaces; oos.writeInt( querySpaces.size() ); // these are always String, why we treat them as Serializable instead is beyond me... - for ( Serializable querySpace : querySpaces ) { + for ( Serializable querySpace : qs ) { oos.writeUTF( querySpace.toString() ); } } @@ -329,10 +332,12 @@ public class ExecutableList this.querySpaces = null; } else { - querySpaces = CollectionHelper.setOfSize( numberOfQuerySpaces ); + // The line below is for CF nullness checking purposes. + final Set querySpaces = CollectionHelper.setOfSize( numberOfQuerySpaces ); for ( int i = 0; i < numberOfQuerySpaces; i++ ) { querySpaces.add( in.readUTF() ); } + this.querySpaces = querySpaces; } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/ExecuteUpdateResultCheckStyle.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/ExecuteUpdateResultCheckStyle.java index b8a97a285c..796101acae 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/ExecuteUpdateResultCheckStyle.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/ExecuteUpdateResultCheckStyle.java @@ -8,6 +8,8 @@ package org.hibernate.engine.spi; import org.hibernate.annotations.ResultCheckStyle; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * For persistence operations (INSERT, UPDATE, DELETE) what style of * determining results (success/failure) is to be used. @@ -54,7 +56,7 @@ public enum ExecuteUpdateResultCheckStyle { return name; } - public static ExecuteUpdateResultCheckStyle fromResultCheckStyle(ResultCheckStyle style) { + public static @Nullable ExecuteUpdateResultCheckStyle fromResultCheckStyle(ResultCheckStyle style) { switch (style) { case NONE: return NONE; @@ -67,7 +69,7 @@ public enum ExecuteUpdateResultCheckStyle { } } - public static ExecuteUpdateResultCheckStyle fromExternalName(String name) { + public static @Nullable ExecuteUpdateResultCheckStyle fromExternalName(String name) { if ( name.equalsIgnoreCase( NONE.name ) ) { return NONE; } @@ -82,7 +84,7 @@ public enum ExecuteUpdateResultCheckStyle { } } - public static ExecuteUpdateResultCheckStyle determineDefault(String customSql, boolean callable) { + public static ExecuteUpdateResultCheckStyle determineDefault(@Nullable String customSql, boolean callable) { return customSql != null && callable ? PARAM : COUNT; } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/FilterDefinition.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/FilterDefinition.java index a7593a9c1c..aee46dea51 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/FilterDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/FilterDefinition.java @@ -13,6 +13,8 @@ import java.util.Set; import org.hibernate.metamodel.mapping.JdbcMapping; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Represents the definition of a {@link org.hibernate.Filter filter}. * This information includes the {@linkplain #filterName name} of the @@ -36,7 +38,7 @@ public class FilterDefinition implements Serializable { * * @param name The name of the filter for which this configuration is in effect. */ - public FilterDefinition(String name, String defaultCondition, Map explicitParamJaMappings) { + public FilterDefinition(String name, String defaultCondition, @Nullable Map explicitParamJaMappings) { this.filterName = name; this.defaultFilterCondition = defaultCondition; if ( explicitParamJaMappings != null ) { @@ -59,7 +61,9 @@ public class FilterDefinition implements Serializable { * @return The parameters named by this configuration. */ public Set getParameterNames() { - return explicitParamJaMappings.keySet(); + // local variable instantiated for checkerframework nullness inference reasons + Set keys = explicitParamJaMappings.keySet(); + return keys; } /** @@ -69,7 +73,7 @@ public class FilterDefinition implements Serializable { * * @return The type of the named parameter. */ - public JdbcMapping getParameterJdbcMapping(String parameterName) { + public @Nullable JdbcMapping getParameterJdbcMapping(String parameterName) { return explicitParamJaMappings.get( parameterName ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/IdentifierValue.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/IdentifierValue.java index aebc6487ae..ff22bcaaa3 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/IdentifierValue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/IdentifierValue.java @@ -12,6 +12,8 @@ import org.hibernate.internal.CoreLogging; import org.jboss.logging.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A strategy for determining if an identifier value is an identifier of * a new transient instance or a previously persistent transient instance. @@ -23,7 +25,7 @@ import org.jboss.logging.Logger; public class IdentifierValue implements UnsavedValueStrategy { private static final Logger LOG = CoreLogging.logger( IdentifierValue.class ); - private final Object value; + private final @Nullable Object value; /** * Always assume the transient instance is newly instantiated @@ -73,13 +75,13 @@ public class IdentifierValue implements UnsavedValueStrategy { */ public static final IdentifierValue NULL = new IdentifierValue() { @Override - public Boolean isUnsaved(Object id) { + public Boolean isUnsaved(@Nullable Object id) { LOG.trace( "ID unsaved-value strategy NULL" ); return id == null; } @Override - public Serializable getDefaultValue(Object currentValue) { + public @Nullable Serializable getDefaultValue(Object currentValue) { return null; } @@ -94,13 +96,13 @@ public class IdentifierValue implements UnsavedValueStrategy { */ public static final IdentifierValue UNDEFINED = new IdentifierValue() { @Override - public Boolean isUnsaved(Object id) { + public @Nullable Boolean isUnsaved(Object id) { LOG.trace( "ID unsaved-value strategy UNDEFINED" ); return null; } @Override - public Serializable getDefaultValue(Object currentValue) { + public @Nullable Serializable getDefaultValue(Object currentValue) { return null; } @@ -126,13 +128,13 @@ public class IdentifierValue implements UnsavedValueStrategy { * Does the given identifier belong to a new instance? */ @Override - public Boolean isUnsaved(Object id) { + public @Nullable Boolean isUnsaved(@Nullable Object id) { LOG.tracev( "ID unsaved-value: {0}", value ); return id == null || id.equals( value ); } @Override - public Object getDefaultValue(Object currentValue) { + public @Nullable Object getDefaultValue(@Nullable Object currentValue) { return value; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/LoadQueryInfluencers.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/LoadQueryInfluencers.java index 1f4a86247d..2ce60823cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/LoadQueryInfluencers.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/LoadQueryInfluencers.java @@ -23,10 +23,13 @@ import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.internal.FilterImpl; import org.hibernate.internal.SessionCreationOptions; +import org.hibernate.internal.util.NullnessUtil; import org.hibernate.loader.ast.spi.CascadingFetchProfile; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; +import org.checkerframework.checker.nullness.qual.Nullable; + import static java.util.Collections.emptySet; import static org.hibernate.engine.FetchStyle.SUBSELECT; @@ -51,7 +54,7 @@ public class LoadQueryInfluencers implements Serializable { @Deprecated(forRemoval = true) public static final LoadQueryInfluencers NONE = new LoadQueryInfluencers(); - private final SessionFactoryImplementor sessionFactory; + private final @Nullable SessionFactoryImplementor sessionFactory; private CascadingFetchProfile enabledCascadingFetchProfile; @@ -85,7 +88,7 @@ public class LoadQueryInfluencers implements Serializable { subselectFetchEnabled = options.isSubselectFetchEnabled(); } - public EffectiveEntityGraph applyEntityGraph(RootGraphImplementor rootGraph, GraphSemantic graphSemantic) { + public EffectiveEntityGraph applyEntityGraph(@Nullable RootGraphImplementor rootGraph, @Nullable GraphSemantic graphSemantic) { final EffectiveEntityGraph effectiveEntityGraph = getEffectiveEntityGraph(); if ( graphSemantic != null ) { if ( rootGraph == null ) { @@ -96,7 +99,7 @@ public class LoadQueryInfluencers implements Serializable { return effectiveEntityGraph; } - public SessionFactoryImplementor getSessionFactory() { + public @Nullable SessionFactoryImplementor getSessionFactory() { return sessionFactory; } @@ -186,7 +189,7 @@ public class LoadQueryInfluencers implements Serializable { } } - public Filter getEnabledFilter(String filterName) { + public @Nullable Filter getEnabledFilter(String filterName) { if ( enabledFilters == null ) { return null; } @@ -197,7 +200,7 @@ public class LoadQueryInfluencers implements Serializable { public Filter enableFilter(String filterName) { checkMutability(); - FilterImpl filter = new FilterImpl( sessionFactory.getFilterDefinition( filterName ) ); + FilterImpl filter = new FilterImpl( NullnessUtil.castNonNull( sessionFactory ).getFilterDefinition( filterName ) ); if ( enabledFilters == null ) { this.enabledFilters = new HashMap<>(); } @@ -223,7 +226,7 @@ public class LoadQueryInfluencers implements Serializable { return filter.getParameter( parsed[1] ); } - public static String[] parseFilterParameterName(String filterParameterName) { + public static String [] parseFilterParameterName(String filterParameterName) { int dot = filterParameterName.lastIndexOf( '.' ); if ( dot <= 0 ) { throw new IllegalArgumentException( @@ -274,7 +277,7 @@ public class LoadQueryInfluencers implements Serializable { } @Internal - public HashSet adjustFetchProfiles(Set disabledFetchProfiles, Set enabledFetchProfiles) { + public @Nullable HashSet adjustFetchProfiles(@Nullable Set disabledFetchProfiles, @Nullable Set enabledFetchProfiles) { final HashSet oldFetchProfiles = hasEnabledFetchProfiles() ? new HashSet<>( enabledFetchProfileNames ) : null; if ( disabledFetchProfiles != null && enabledFetchProfileNames != null ) { @@ -353,7 +356,7 @@ public class LoadQueryInfluencers implements Serializable { private boolean isSubselectFetchEnabledInProfile(CollectionPersister persister) { if ( hasEnabledFetchProfiles() ) { for ( String profile : getEnabledFetchProfileNames() ) { - final FetchProfile fetchProfile = sessionFactory.getFetchProfile( profile ); + final FetchProfile fetchProfile = persister.getFactory().getFetchProfile( profile ) ; if ( fetchProfile != null ) { final Fetch fetch = fetchProfile.getFetchByRole( persister.getRole() ); if ( fetch != null && fetch.getMethod() == SUBSELECT) { @@ -382,7 +385,7 @@ public class LoadQueryInfluencers implements Serializable { private boolean hasSubselectLoadableCollectionsEnabledInProfile(EntityPersister persister) { if ( hasEnabledFetchProfiles() ) { for ( String profile : getEnabledFetchProfileNames() ) { - if ( sessionFactory.getFetchProfile( profile ) + if ( persister.getFactory().getFetchProfile( profile ) .hasSubselectLoadableCollectionsEnabled( persister ) ) { return true; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/PrimeAmongSecondarySupertypes.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/PrimeAmongSecondarySupertypes.java index d715faba16..691aa45a2e 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/PrimeAmongSecondarySupertypes.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/PrimeAmongSecondarySupertypes.java @@ -11,6 +11,8 @@ import org.hibernate.engine.internal.ManagedTypeHelper; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.ProxyConfiguration; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * For a full explanation of the purpose of this interface see {@link ManagedTypeHelper}. * @@ -22,46 +24,46 @@ import org.hibernate.proxy.ProxyConfiguration; @Internal public interface PrimeAmongSecondarySupertypes { - default ManagedEntity asManagedEntity() { + default @Nullable ManagedEntity asManagedEntity() { return null; } - default PersistentAttributeInterceptable asPersistentAttributeInterceptable() { + default @Nullable PersistentAttributeInterceptable asPersistentAttributeInterceptable() { return null; } - default SelfDirtinessTracker asSelfDirtinessTracker() { + default @Nullable SelfDirtinessTracker asSelfDirtinessTracker() { return null; } //Included for consistency but doesn't seem to be used? - default Managed asManaged() { + default @Nullable Managed asManaged() { return null; } //Included for consistency but doesn't seem to be used? - default ManagedComposite asManagedComposite() { + default @Nullable ManagedComposite asManagedComposite() { return null; } //Included for consistency but doesn't seem to be used? - default ManagedMappedSuperclass asManagedMappedSuperclass() { + default @Nullable ManagedMappedSuperclass asManagedMappedSuperclass() { return null; } - default CompositeOwner asCompositeOwner() { + default @Nullable CompositeOwner asCompositeOwner() { return null; } - default CompositeTracker asCompositeTracker() { + default @Nullable CompositeTracker asCompositeTracker() { return null; } - default HibernateProxy asHibernateProxy() { + default @Nullable HibernateProxy asHibernateProxy() { return null; } - default ProxyConfiguration asProxyConfiguration() { + default @Nullable ProxyConfiguration asProxyConfiguration() { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java index e2da4601fa..27c60a7976 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java @@ -68,6 +68,7 @@ import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; import jakarta.persistence.metamodel.Metamodel; +import org.checkerframework.checker.nullness.qual.Nullable; /** * A wrapper class that delegates all method invocations to a delegate instance of @@ -165,7 +166,7 @@ public class SessionDelegatorBaseImpl implements SessionImplementor { } @Override - public EntityPersister getEntityPersister(String entityName, Object object) throws HibernateException { + public EntityPersister getEntityPersister(@Nullable String entityName, Object object) throws HibernateException { return delegate.getEntityPersister( entityName, object ); } @@ -853,22 +854,22 @@ public class SessionDelegatorBaseImpl implements SessionImplementor { } @Override - public T find(Class entityClass, Object primaryKey) { + public @Nullable T find(Class entityClass, Object primaryKey) { return delegate.find( entityClass, primaryKey ); } @Override - public T find(Class entityClass, Object primaryKey, Map properties) { + public @Nullable T find(Class entityClass, Object primaryKey, Map properties) { return delegate.find( entityClass, primaryKey, properties ); } @Override - public T find(Class entityClass, Object primaryKey, LockModeType lockMode) { + public @Nullable T find(Class entityClass, Object primaryKey, LockModeType lockMode) { return delegate.find( entityClass, primaryKey, lockMode ); } @Override - public T find(Class entityClass, Object primaryKey, LockModeType lockMode, Map properties) { + public @Nullable T find(Class entityClass, Object primaryKey, LockModeType lockMode, Map properties) { return delegate.find( entityClass, primaryKey, lockMode, properties ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java index 1fce375e30..b5b9a805c5 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java @@ -51,6 +51,7 @@ import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; import jakarta.persistence.metamodel.Metamodel; +import org.checkerframework.checker.nullness.qual.Nullable; /** * This helper class allows decorating a Session instance, while the @@ -809,22 +810,22 @@ public class SessionLazyDelegator implements Session { } @Override - public T find(Class entityClass, Object primaryKey) { + public @Nullable T find(Class entityClass, Object primaryKey) { return this.lazySession.get().find( entityClass, primaryKey ); } @Override - public T find(Class entityClass, Object primaryKey, Map properties) { + public @Nullable T find(Class entityClass, Object primaryKey, Map properties) { return this.lazySession.get().find( entityClass, primaryKey, properties ); } @Override - public T find(Class entityClass, Object primaryKey, LockModeType lockMode) { + public @Nullable T find(Class entityClass, Object primaryKey, LockModeType lockMode) { return this.lazySession.get().find( entityClass, primaryKey, lockMode ); } @Override - public T find(Class entityClass, Object primaryKey, LockModeType lockMode, Map properties) { + public @Nullable T find(Class entityClass, Object primaryKey, LockModeType lockMode, Map properties) { return this.lazySession.get().find( entityClass, primaryKey, lockMode, properties ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java index a3289c8063..3423419d72 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java @@ -10,6 +10,7 @@ import java.util.Set; import java.util.UUID; import jakarta.persistence.FlushModeType; import jakarta.persistence.TransactionRequiredException; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.CacheMode; import org.hibernate.FlushMode; @@ -307,7 +308,7 @@ public interface SharedSessionContractImplementor * @param entityName optional entity name * @param object the entity instance */ - EntityPersister getEntityPersister(String entityName, Object object) throws HibernateException; + EntityPersister getEntityPersister(@Nullable String entityName, Object object) throws HibernateException; /** * Get the entity instance associated with the given {@link EntityKey}, diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java index acf5f624f6..0d3fff3f7f 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java @@ -11,6 +11,8 @@ import jakarta.persistence.FlushModeType; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; +import org.checkerframework.checker.nullness.qual.Nullable; + import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.HibernateException; @@ -413,7 +415,7 @@ public class SharedSessionDelegatorBaseImpl implements SharedSessionContractImpl } @Override - public EntityPersister getEntityPersister(String entityName, Object object) throws HibernateException { + public EntityPersister getEntityPersister(@Nullable String entityName, Object object) throws HibernateException { return delegate.getEntityPersister( entityName, object ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/TypedValue.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/TypedValue.java index 62d8ee7fbf..a172f62714 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/TypedValue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/TypedValue.java @@ -13,6 +13,8 @@ import java.io.Serializable; import org.hibernate.internal.util.ValueHolder; import org.hibernate.type.Type; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * An ordered pair of a value and its Hibernate type. * @@ -28,7 +30,7 @@ public final class TypedValue implements Serializable { public TypedValue(final Type type, final Object value) { this.type = type; this.value = value; - initTransients(); + this.hashcode = hashCode(type, value); } public Object getValue() { @@ -47,7 +49,7 @@ public final class TypedValue implements Serializable { return hashcode.getValue(); } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if ( this == other ) { return true; } @@ -62,10 +64,10 @@ public final class TypedValue implements Serializable { private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { ois.defaultReadObject(); - initTransients(); + this.hashcode = hashCode(type, value); } - private void initTransients() { - this.hashcode = new ValueHolder<>( () -> value == null ? 0 : type.getHashCode( value ) ); + private static ValueHolder hashCode(Type type, Object value) { + return new ValueHolder<>( () -> value == null ? 0 : type.getHashCode( value ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/UnsavedValueStrategy.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/UnsavedValueStrategy.java index 9cc71de31f..6b5eee58d8 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/UnsavedValueStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/UnsavedValueStrategy.java @@ -6,6 +6,8 @@ */ package org.hibernate.engine.spi; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * The base contract for determining transient status versus detached status. * @@ -21,7 +23,7 @@ public interface UnsavedValueStrategy { * indicates the value does not corresponds to unsaved data (aka, detached state); {@code null} indicates that * this strategy was not able to determine conclusively. */ - Boolean isUnsaved(Object test); + @Nullable Boolean isUnsaved(@Nullable Object test); /** * Get a default value meant to indicate transience. @@ -30,5 +32,5 @@ public interface UnsavedValueStrategy { * * @return The default transience value. */ - Object getDefaultValue(Object currentValue); + @Nullable Object getDefaultValue(@Nullable Object currentValue); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/VersionValue.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/VersionValue.java index 8876693007..27a7571ff8 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/VersionValue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/VersionValue.java @@ -12,6 +12,8 @@ import org.hibernate.internal.CoreLogging; import org.jboss.logging.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * A strategy for determining if a version value is a version of * a new transient instance or a previously persistent transient instance. @@ -23,20 +25,20 @@ import org.jboss.logging.Logger; public class VersionValue implements UnsavedValueStrategy { private static final Logger LOG = CoreLogging.logger( VersionValue.class ); - private final Object value; + private final @Nullable Object value; /** * Assume the transient instance is newly instantiated if the version * is null, otherwise assume it is a detached instance. */ public static final VersionValue NULL = new VersionValue() { @Override - public Boolean isUnsaved(Object version) { + public Boolean isUnsaved(@Nullable Object version) { LOG.trace( "Version unsaved-value strategy NULL" ); return version == null; } @Override - public Object getDefaultValue(Object currentValue) { + public @Nullable Object getDefaultValue(Object currentValue) { return null; } @@ -52,7 +54,7 @@ public class VersionValue implements UnsavedValueStrategy { */ public static final VersionValue UNDEFINED = new VersionValue() { @Override - public Boolean isUnsaved(Object version) { + public @Nullable Boolean isUnsaved(@Nullable Object version) { LOG.trace( "Version unsaved-value strategy UNDEFINED" ); return version == null ? Boolean.TRUE : null; } @@ -75,7 +77,7 @@ public class VersionValue implements UnsavedValueStrategy { public static final VersionValue NEGATIVE = new VersionValue() { @Override - public Boolean isUnsaved(Object version) throws MappingException { + public Boolean isUnsaved(@Nullable Object version) throws MappingException { LOG.trace( "Version unsaved-value strategy NEGATIVE" ); if ( version == null ) { return Boolean.TRUE; @@ -114,13 +116,13 @@ public class VersionValue implements UnsavedValueStrategy { } @Override - public Boolean isUnsaved(Object version) throws MappingException { + public @Nullable Boolean isUnsaved(@Nullable Object version) throws MappingException { LOG.tracev( "Version unsaved-value: {0}", value ); return version == null || version.equals( value ); } @Override - public Object getDefaultValue(Object currentValue) { + public @Nullable Object getDefaultValue(@Nullable Object currentValue) { return value; } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/spi/AppliedGraph.java b/hibernate-core/src/main/java/org/hibernate/graph/spi/AppliedGraph.java index a276146772..6b8c36bba6 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/spi/AppliedGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/spi/AppliedGraph.java @@ -8,6 +8,8 @@ package org.hibernate.graph.spi; import org.hibernate.graph.GraphSemantic; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Contract for anything a fetch/load graph can be applied * @@ -17,10 +19,10 @@ public interface AppliedGraph { /** * The applied graph */ - RootGraphImplementor getGraph(); + @Nullable RootGraphImplementor getGraph(); /** * The semantic (fetch/load) under which the graph should be applied */ - GraphSemantic getSemantic(); + @Nullable GraphSemantic getSemantic(); } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java index 62019274f7..f989186fca 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java @@ -42,7 +42,7 @@ public final class StringHelper { return string.length() - 1; } - public static String join(String seperator, String[] strings) { + public static String join(String separator, String[] strings) { int length = strings.length; if ( length == 0 ) { return ""; @@ -54,7 +54,7 @@ public final class StringHelper { StringBuilder buf = new StringBuilder( length * firstStringLength ) .append( strings[0] ); for ( int i = 1; i < length; i++ ) { - buf.append( seperator ).append( strings[i] ); + buf.append( separator ).append( strings[i] ); } return buf.toString(); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ModelPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ModelPart.java index c10fa072d3..176c4ab8d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ModelPart.java @@ -19,6 +19,8 @@ import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.type.descriptor.java.JavaType; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Base descriptor, within the mapping model, for any part of the * application's domain model: an attribute, an entity identifier, @@ -200,7 +202,7 @@ public interface ModelPart extends MappingModelExpressible { EntityMappingType findContainingEntityMapping(); - default boolean areEqual(Object one, Object other, SharedSessionContractImplementor session) { + default boolean areEqual(@Nullable Object one, @Nullable Object other, SharedSessionContractImplementor session) { // NOTE : deepEquals to account for arrays (compound natural-id) return Objects.deepEquals( one, other ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryOptionsImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryOptionsImpl.java index 6a6776948d..38166ca190 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryOptionsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryOptionsImpl.java @@ -14,6 +14,7 @@ import java.util.Set; import jakarta.persistence.CacheRetrieveMode; import jakarta.persistence.CacheStoreMode; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.FlushMode; import org.hibernate.LockOptions; @@ -248,12 +249,12 @@ public class QueryOptionsImpl implements MutableQueryOptions, AppliedGraph { } @Override - public RootGraphImplementor getGraph() { + public @Nullable RootGraphImplementor getGraph() { return rootGraph; } @Override - public GraphSemantic getSemantic() { + public @Nullable GraphSemantic getSemantic() { return graphSemantic; } }