From 7c61e20f87de824fcbe460c78a4be7f7fb4cb5b6 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 25 Nov 2025 12:00:33 +0100 Subject: [PATCH 001/181] update to latest asciidoc theme --- local-build-plugins/src/main/groovy/local.javadoc.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local-build-plugins/src/main/groovy/local.javadoc.gradle b/local-build-plugins/src/main/groovy/local.javadoc.gradle index fbd6ee37e912..d2b8ac96b59b 100644 --- a/local-build-plugins/src/main/groovy/local.javadoc.gradle +++ b/local-build-plugins/src/main/groovy/local.javadoc.gradle @@ -11,7 +11,7 @@ configurations { } dependencies { - themezip 'org.hibernate.infra:hibernate-asciidoctor-theme:6.1.1.Final@zip' + themezip 'org.hibernate.infra:hibernate-asciidoctor-theme:6.1.2.Final@zip' } def unpackThemeTask = tasks.register('unpackTheme', Copy) { From 79d98c24d736d2f8d7a25ec4bc6cb9a5b551cb34 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 25 Nov 2025 13:42:15 +0100 Subject: [PATCH 002/181] more refactoring to ModelBinder --- .../hbm/AbstractEntitySourceImpl.java | 15 +- .../internal/hbm/EntityNamingSourceImpl.java | 10 +- .../source/internal/hbm/ModelBinder.java | 897 ++++++++++-------- 3 files changed, 500 insertions(+), 422 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/AbstractEntitySourceImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/AbstractEntitySourceImpl.java index b8cc59f58235..2c2addd58c71 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/AbstractEntitySourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/AbstractEntitySourceImpl.java @@ -41,6 +41,8 @@ import org.hibernate.internal.util.collections.CollectionHelper; import static java.util.Collections.emptyMap; +import static org.hibernate.internal.util.StringHelper.isNotEmpty; +import static org.hibernate.internal.util.StringHelper.unqualify; /** * @author Steve Ebersole @@ -97,19 +99,20 @@ protected AbstractEntitySourceImpl(MappingDocument sourceMappingDocument, JaxbHb ); } - public static EntityNamingSourceImpl extractEntityNamingSource( + static EntityNamingSourceImpl extractEntityNamingSource( MappingDocument sourceMappingDocument, EntityInfo jaxbEntityMapping) { final String className = sourceMappingDocument.qualifyClassName( jaxbEntityMapping.getName() ); + final String mappingEntityName = jaxbEntityMapping.getEntityName(); final String entityName; final String jpaEntityName; - if ( StringHelper.isNotEmpty( jaxbEntityMapping.getEntityName() ) ) { - entityName = jaxbEntityMapping.getEntityName(); - jpaEntityName = jaxbEntityMapping.getEntityName(); + if ( isNotEmpty( mappingEntityName ) ) { + entityName = mappingEntityName; + jpaEntityName = mappingEntityName; } else { entityName = className; - jpaEntityName = StringHelper.unqualify( className ); + jpaEntityName = unqualify( className ); } return new EntityNamingSourceImpl( entityName, className, jpaEntityName ); } @@ -122,7 +125,7 @@ private FilterSource[] buildFilterSources() { return NO_FILTER_SOURCES; } - FilterSource[] results = new FilterSource[size]; + final var results = new FilterSource[size]; for ( int i = 0; i < size; i++ ) { JaxbHbmFilterType element = jaxbClassElement.getFilter().get( i ); results[i] = new FilterSourceImpl( sourceMappingDocument(), element ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/EntityNamingSourceImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/EntityNamingSourceImpl.java index 68bc41743838..dd9cc77701be 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/EntityNamingSourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/EntityNamingSourceImpl.java @@ -5,9 +5,10 @@ package org.hibernate.boot.model.source.internal.hbm; import org.hibernate.boot.model.source.spi.EntityNamingSource; -import org.hibernate.internal.util.StringHelper; import org.hibernate.mapping.PersistentClass; +import static org.hibernate.internal.util.StringHelper.isNotEmpty; + /** * Implementation of EntityNamingSource * @@ -24,12 +25,13 @@ public EntityNamingSourceImpl(String entityName, String className, String jpaEnt this.entityName = entityName; this.className = className; this.jpaEntityName = jpaEntityName; - - this.typeName = StringHelper.isNotEmpty( className ) ? className : entityName; + this.typeName = isNotEmpty( className ) ? className : entityName; } public EntityNamingSourceImpl(PersistentClass entityBinding) { - this( entityBinding.getEntityName(), entityBinding.getClassName(), entityBinding.getJpaEntityName() ); + this( entityBinding.getEntityName(), + entityBinding.getClassName(), + entityBinding.getJpaEntityName() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java index 4277507b3062..9cdaa0178438 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java @@ -45,6 +45,7 @@ import org.hibernate.boot.spi.NaturalIdUniqueKeyBinder; import org.hibernate.boot.spi.SecondPass; import org.hibernate.cache.spi.access.AccessType; +import org.hibernate.dialect.Dialect; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.generator.internal.GeneratedGeneration; @@ -80,6 +81,7 @@ import org.hibernate.mapping.SingleTableSubclass; import org.hibernate.mapping.SortableValue; import org.hibernate.mapping.Table; +import org.hibernate.mapping.ToOne; import org.hibernate.mapping.UnionSubclass; import org.hibernate.mapping.UniqueKey; import org.hibernate.mapping.Value; @@ -470,7 +472,7 @@ private void bindJoinedSubclassEntity( keyBinding.makeNationalized(); } entityDescriptor.setKey( keyBinding ); - keyBinding.setOnDeleteAction( getOnDeleteAction( entitySource.isCascadeDeleteEnabled() ) ); + keyBinding.setOnDeleteAction( getOnDeleteAction( entitySource ) ); relationalObjectBinder.bindColumns( mappingDocument, entitySource.getPrimaryKeyColumnSources(), @@ -486,7 +488,8 @@ public Identifier determineImplicitName(LocalMetadataBuildingContext context) { } ); keyBinding.sortProperties(); - setForeignKeyName( keyBinding, entitySource.getExplicitForeignKeyName() ); + setForeignKeyName( keyBinding, + entitySource.getExplicitForeignKeyName() ); // model.getKey().setType( new Type( model.getIdentifier() ) ); entityDescriptor.createPrimaryKey(); entityDescriptor.createForeignKey(); @@ -1209,9 +1212,10 @@ private void bindCollectionMetadata(MappingDocument mappingDocument, PluralAttri binding.setTypeName( typeName ); binding.setTypeParameters( typeParameters ); - if ( source.getFetchCharacteristics().getFetchTiming() == FetchTiming.DELAYED ) { + final var fetchCharacteristics = source.getFetchCharacteristics(); + if ( fetchCharacteristics.getFetchTiming() == FetchTiming.DELAYED ) { binding.setLazy( true ); - binding.setExtraLazy( source.getFetchCharacteristics().isExtraLazy() ); + binding.setExtraLazy( fetchCharacteristics.isExtraLazy() ); } else { binding.setLazy( false ); @@ -1293,7 +1297,8 @@ private static void bindCustomSql(PluralAttributeSource source, Collection bindi } private static void setupFetching(PluralAttributeSource source, Collection binding) { - switch ( source.getFetchCharacteristics().getFetchStyle() ) { + final var fetchCharacteristics = source.getFetchCharacteristics(); + switch ( fetchCharacteristics.getFetchStyle() ) { case SELECT: binding.setFetchMode( FetchMode.SELECT ); break; @@ -1302,7 +1307,7 @@ private static void setupFetching(PluralAttributeSource source, Collection bindi break; case BATCH: binding.setFetchMode( FetchMode.SELECT ); - binding.setBatchSize( source.getFetchCharacteristics().getBatchSize() ); + binding.setBatchSize( fetchCharacteristics.getBatchSize() ); break; case SUBSELECT: binding.setFetchMode( FetchMode.SELECT ); @@ -1312,7 +1317,7 @@ private static void setupFetching(PluralAttributeSource source, Collection bindi break; default: throw new AssertionFailure( "Unexpected FetchStyle : " - + source.getFetchCharacteristics().getFetchStyle().name() ); + + fetchCharacteristics.getFetchStyle().name() ); } } @@ -1385,7 +1390,8 @@ private Identifier determineTable( for ( var relationalValueSource : relationalValueSources ) { // We need to get the containing table name for both columns and formulas, // particularly when a column/formula is for a property on a secondary table. - if ( !Objects.equals( tableName, relationalValueSource.getContainingTableName() ) ) { + final String containingTableName = relationalValueSource.getContainingTableName(); + if ( !Objects.equals( tableName, containingTableName ) ) { if ( tableName != null ) { throw new MappingException( String.format( @@ -1393,12 +1399,12 @@ private Identifier determineTable( "Attribute [%s] referenced columns from multiple tables: %s, %s", attributeName, tableName, - relationalValueSource.getContainingTableName() + containingTableName ), mappingDocument.getOrigin() ); } - tableName = relationalValueSource.getContainingTableName(); + tableName = containingTableName; } } return database.toIdentifier( tableName ); @@ -1429,8 +1435,7 @@ private void bindSecondaryTable( } secondaryTable.setComment( tableSource.getComment() ); } - else { - final var inLineViewSource = (InLineViewSource) tableSpecificationSource; + else if ( tableSpecificationSource instanceof InLineViewSource inLineViewSource ) { logicalTableName = toIdentifier( inLineViewSource.getLogicalName() ); secondaryTable = new Table( metadataBuildingContext.getCurrentContributorName(), @@ -1439,6 +1444,9 @@ private void bindSecondaryTable( false ); } + else { + throw new AssertionFailure( "Unexpected table specification soure type" ); + } secondaryTableJoin.setTable( secondaryTable ); entityTableXref.addSecondaryTable( mappingDocument, logicalTableName, secondaryTableJoin ); @@ -1456,13 +1464,14 @@ private void bindSecondaryTable( } final var keyBinding = - new DependantValue( mappingDocument, secondaryTable, persistentClass.getIdentifier() ); + new DependantValue( mappingDocument, secondaryTable, + persistentClass.getIdentifier() ); if ( mappingDocument.getBuildingOptions().useNationalizedCharacterData() ) { keyBinding.makeNationalized(); } secondaryTableJoin.setKey( keyBinding ); - keyBinding.setOnDeleteAction( getOnDeleteAction( secondaryTableSource.isCascadeDeleteEnabled() ) ); + keyBinding.setOnDeleteAction( getOnDeleteAction( secondaryTableSource ) ); // NOTE: no Type info to bind... @@ -1482,7 +1491,8 @@ public Identifier determineImplicitName(LocalMetadataBuildingContext context) { ); keyBinding.sortProperties(); - setForeignKeyName( keyBinding, secondaryTableSource.getExplicitForeignKeyName() ); + setForeignKeyName( keyBinding, + secondaryTableSource.getExplicitForeignKeyName() ); // skip creating primary and foreign keys for a subselect. if ( secondaryTable.getSubselect() == null ) { @@ -1633,15 +1643,8 @@ private Property createOneToOneAttribute( oneToOneSource.getAttributeRole() ); - final String propertyRef = oneToOneBinding.getReferencedPropertyName(); - if ( propertyRef != null ) { - handlePropertyReference( - sourceDocument, - oneToOneBinding.getReferencedEntityName(), - propertyRef, - "" - ); - } + handlePropertyRef( sourceDocument, oneToOneBinding, + "" ); // Defer the creation of the foreign key as we need the // associated entity persister to be initialized so that @@ -1656,25 +1659,36 @@ private Property createOneToOneAttribute( return property; } + private void handlePropertyRef( + MappingDocument sourceDocument, + ToOne binding, + String synopsis) { + final String propertyRef = binding.getReferencedPropertyName(); + if ( propertyRef != null ) { + handlePropertyReference( + sourceDocument, + binding.getReferencedEntityName(), + propertyRef, + synopsis + ); + } + } + private void handlePropertyReference( MappingDocument mappingDocument, String referencedEntityName, String referencedPropertyName, String sourceElementSynopsis) { - final var entityBinding = - mappingDocument.getMetadataCollector() - .getEntityBinding( referencedEntityName ); + final var collector = mappingDocument.getMetadataCollector(); + final var entityBinding = collector.getEntityBinding( referencedEntityName ); if ( entityBinding == null ) { // entity may just not have been processed yet - set up a delayed handler registerDelayedPropertyReferenceHandler( - new DelayedPropertyReferenceHandlerImpl( - referencedEntityName, - referencedPropertyName, - true, - sourceElementSynopsis, - mappingDocument.getOrigin() - ), - mappingDocument + mappingDocument, + referencedEntityName, + referencedPropertyName, + sourceElementSynopsis, + collector ); } else { @@ -1683,14 +1697,11 @@ private void handlePropertyReference( if ( propertyBinding == null ) { // attribute may just not have been processed yet - set up a delayed handler registerDelayedPropertyReferenceHandler( - new DelayedPropertyReferenceHandlerImpl( - referencedEntityName, - referencedPropertyName, - true, - sourceElementSynopsis, - mappingDocument.getOrigin() - ), - mappingDocument + mappingDocument, + referencedEntityName, + referencedPropertyName, + sourceElementSynopsis, + collector ); } else { @@ -1706,15 +1717,26 @@ private void handlePropertyReference( } private void registerDelayedPropertyReferenceHandler( - DelayedPropertyReferenceHandlerImpl handler, - MetadataBuildingContext buildingContext) { + MappingDocument mappingDocument, + String referencedEntityName, + String referencedPropertyName, + String sourceElementSynopsis, + InFlightMetadataCollector collector) { + final var handler = + new DelayedPropertyReferenceHandlerImpl( + referencedEntityName, + referencedPropertyName, + true, + sourceElementSynopsis, + mappingDocument.getOrigin() + ); BOOT_LOGGER.tracef( "Property [%s.%s] referenced by property-ref [%s] was not yet available - creating delayed handler", handler.referencedEntityName, handler.referencedPropertyName, handler.sourceElementSynopsis ); - buildingContext.getMetadataCollector().addDelayedPropertyReferenceHandler( handler ); + collector.addDelayedPropertyReferenceHandler( handler ); } public void bindOneToOne( @@ -1729,19 +1751,8 @@ public void bindOneToOne( oneToOneBinding ); - if ( oneToOneSource.isConstrained() ) { - final String cascadeStyleName = oneToOneSource.getCascadeStyleName(); - if ( cascadeStyleName != null && cascadeStyleName.contains( "delete-orphan" ) ) { - throw new MappingException( - String.format( - Locale.ENGLISH, - "one-to-one attribute [%s] cannot specify orphan delete cascading as it is constrained", - oneToOneSource.getAttributeRole().getFullPath() - ), - sourceDocument.getOrigin() - ); - } + checkConstrainedOneToOneOrphanDelete( sourceDocument, oneToOneSource ); oneToOneBinding.setConstrained( true ); oneToOneBinding.setForeignKeyType( ForeignKeyDirection.FROM_PARENT ); } @@ -1749,15 +1760,7 @@ public void bindOneToOne( oneToOneBinding.setForeignKeyType( ForeignKeyDirection.TO_PARENT ); } - final var fetchCharacteristics = oneToOneSource.getFetchCharacteristics(); - oneToOneBinding.setLazy( fetchCharacteristics.getFetchTiming() == FetchTiming.DELAYED ); - oneToOneBinding.setFetchMode( - fetchCharacteristics.getFetchStyle() == FetchStyle.SELECT - ? FetchMode.SELECT - : FetchMode.JOIN - ); - oneToOneBinding.setUnwrapProxy( fetchCharacteristics.isUnwrapProxies() ); - + handleFetchCharacteristics( oneToOneSource, oneToOneBinding ); final String referencedEntityAttributeName = oneToOneSource.getReferencedEntityAttributeName(); if ( isNotEmpty( referencedEntityAttributeName ) ) { @@ -1775,11 +1778,21 @@ public void bindOneToOne( DEPRECATION_LOGGER.logDeprecationOfEmbedXmlSupport(); } - if ( isNotEmpty( oneToOneSource.getExplicitForeignKeyName() ) ) { - setForeignKeyName( oneToOneBinding, oneToOneSource.getExplicitForeignKeyName() ); - } + setForeignKeyName( oneToOneBinding, + oneToOneSource.getExplicitForeignKeyName() ); - oneToOneBinding.setOnDeleteAction( getOnDeleteAction( oneToOneSource.isCascadeDeleteEnabled() ) ); + oneToOneBinding.setOnDeleteAction( getOnDeleteAction( oneToOneSource ) ); + } + + private static void handleFetchCharacteristics(SingularAttributeSourceToOne toOneSource, ToOne toOneBinding) { + final var fetchCharacteristics = toOneSource.getFetchCharacteristics(); + toOneBinding.setLazy( fetchCharacteristics.getFetchTiming() == FetchTiming.DELAYED ); + toOneBinding.setFetchMode( + fetchCharacteristics.getFetchStyle() == FetchStyle.SELECT + ? FetchMode.SELECT + : FetchMode.JOIN + ); + toOneBinding.setUnwrapProxy( fetchCharacteristics.isUnwrapProxies() ); } private Property createManyToOneAttribute( @@ -1787,18 +1800,42 @@ private Property createManyToOneAttribute( SingularAttributeSourceManyToOne manyToOneSource, ManyToOne manyToOneBinding, String containingClassName) { - final String attributeName = manyToOneSource.getName(); + final String referencedEntityName = + handleReferencedEntity( sourceDocument, manyToOneSource, manyToOneBinding, containingClassName ); + + if ( manyToOneSource.isUnique() ) { + manyToOneBinding.markAsLogicalOneToOne(); + } + bindManyToOneAttribute( sourceDocument, manyToOneSource, manyToOneBinding, referencedEntityName ); + + handlePropertyRef( sourceDocument, manyToOneBinding, + "" ); + + final var property = new Property(); + property.setValue( manyToOneBinding ); + bindProperty( sourceDocument, manyToOneSource, property ); + + checkManyToOneOrphanDelete( sourceDocument, manyToOneSource, manyToOneBinding ); + + return property; + } + + private String handleReferencedEntity( + MappingDocument sourceDocument, + SingularAttributeSourceManyToOne manyToOneSource, + ManyToOne manyToOneBinding, + String containingClassName) { final String explicitReferencedEntityName = manyToOneSource.getReferencedEntityName(); - final String referencedEntityName; if ( explicitReferencedEntityName != null ) { - referencedEntityName = explicitReferencedEntityName; + return explicitReferencedEntityName; } else { + final String attributeName = manyToOneSource.getName(); final var reflectedPropertyClass = reflectedPropertyClass( sourceDocument, containingClassName, attributeName ); if ( reflectedPropertyClass != null ) { - referencedEntityName = reflectedPropertyClass.getName(); + return reflectedPropertyClass.getName(); } else { prepareValueTypeViaReflection( @@ -1808,57 +1845,54 @@ private Property createManyToOneAttribute( attributeName, manyToOneSource.getAttributeRole() ); - referencedEntityName = manyToOneBinding.getTypeName(); + return manyToOneBinding.getTypeName(); } } + } - if ( manyToOneSource.isUnique() ) { - manyToOneBinding.markAsLogicalOneToOne(); - } - - bindManyToOneAttribute( sourceDocument, manyToOneSource, manyToOneBinding, referencedEntityName ); - - final String propertyRef = manyToOneBinding.getReferencedPropertyName(); - if ( propertyRef != null ) { - handlePropertyReference( - sourceDocument, - manyToOneBinding.getReferencedEntityName(), - propertyRef, - "" + private static void checkManyToOneOrphanDelete( + MappingDocument sourceDocument, + SingularAttributeSourceManyToOne manyToOneSource, + ManyToOne manyToOneBinding) { + // TODO: would be better to delay this until the end of binding (second pass, etc) + // in order to properly allow for a singular unique column for a many-to-one to + // to also trigger a "logical one-to-one". As is, this can occasionally lead to + // false exceptions if the many-to-one column binding is delayed and the + // uniqueness is indicated on the rather than on the + // + // Ideally, would love to see a SimpleValue#validate approach, rather than a + // SimpleValue#isValid that is then handled at a higher level (Property, etc). + // The reason being that the current approach misses the exact reason a + // "validation" fails since it loses "context" + final String cascadeStyleName = manyToOneSource.getCascadeStyleName(); + if ( cascadeStyleName != null && cascadeStyleName.contains( "delete-orphan" ) + && !manyToOneBinding.isLogicalOneToOne() ) { + throw new MappingException( + String.format( + Locale.ENGLISH, + "many-to-one attribute [%s] specified delete-orphan but is not specified as unique; " + + "remove delete-orphan cascading or specify unique=\"true\"", + manyToOneSource.getAttributeRole().getFullPath() + ), + sourceDocument.getOrigin() ); } + } - final var property = new Property(); - property.setValue( manyToOneBinding ); - bindProperty( sourceDocument, manyToOneSource, property ); - - if ( isNotEmpty( manyToOneSource.getCascadeStyleName() ) ) { - // todo : would be better to delay this the end of binding (second pass, etc) - // in order to properly allow for a singular unique column for a many-to-one to - // also trigger a "logical one-to-one". As-is, this can occasionally lead to - // false exceptions if the many-to-one column binding is delayed and the - // uniqueness is indicated on the rather than on the - // - // Ideally, would love to see a SimpleValue#validate approach, rather than a - // SimpleValue#isValid that is then handled at a higher level (Property, etc). - // The reason being that the current approach misses the exact reason - // a "validation" fails since it loses "context" - if ( manyToOneSource.getCascadeStyleName().contains( "delete-orphan" ) ) { - if ( !manyToOneBinding.isLogicalOneToOne() ) { - throw new MappingException( - String.format( - Locale.ENGLISH, - "many-to-one attribute [%s] specified delete-orphan but is not specified as unique; " + - "remove delete-orphan cascading or specify unique=\"true\"", - manyToOneSource.getAttributeRole().getFullPath() - ), - sourceDocument.getOrigin() - ); - } - } + private static void checkConstrainedOneToOneOrphanDelete( + MappingDocument sourceDocument, + SingularAttributeSourceOneToOne oneToOneSource) { + final String cascadeStyleName = oneToOneSource.getCascadeStyleName(); + if ( cascadeStyleName != null && cascadeStyleName.contains( "delete-orphan" ) ) { + throw new MappingException( + String.format( + Locale.ENGLISH, + "one-to-one attribute [%s] cannot specify orphan delete cascading as it is constrained", + oneToOneSource.getAttributeRole().getFullPath() + ), + sourceDocument.getOrigin() + ); } - - return property; } private void bindManyToOneAttribute( @@ -1868,24 +1902,10 @@ private void bindManyToOneAttribute( String referencedEntityName) { // NOTE: no type information to bind - manyToOneBinding.setReferencedEntityName( referencedEntityName ); - final String referencedEntityAttributeName = manyToOneSource.getReferencedEntityAttributeName(); - if ( isNotEmpty( referencedEntityAttributeName ) ) { - manyToOneBinding.setReferencedPropertyName( referencedEntityAttributeName ); - manyToOneBinding.setReferenceToPrimaryKey( false ); - } - else { - manyToOneBinding.setReferenceToPrimaryKey( true ); - } + handleReferencedEntity( manyToOneBinding, referencedEntityName, + manyToOneSource.getReferencedEntityAttributeName() ); - final var fetchCharacteristics = manyToOneSource.getFetchCharacteristics(); - manyToOneBinding.setLazy( fetchCharacteristics.getFetchTiming() == FetchTiming.DELAYED ); - manyToOneBinding.setUnwrapProxy( fetchCharacteristics.isUnwrapProxies() ); - manyToOneBinding.setFetchMode( - fetchCharacteristics.getFetchStyle() == FetchStyle.SELECT - ? FetchMode.SELECT - : FetchMode.JOIN - ); + handleFetchCharacteristics( manyToOneSource, manyToOneBinding ); if ( manyToOneSource.isEmbedXml() == Boolean.TRUE ) { DEPRECATION_LOGGER.logDeprecationOfEmbedXmlSupport(); @@ -1893,7 +1913,8 @@ private void bindManyToOneAttribute( manyToOneBinding.setIgnoreNotFound( manyToOneSource.isIgnoreNotFound() ); - setForeignKeyName( manyToOneBinding, manyToOneSource.getExplicitForeignKeyName() ); + setForeignKeyName( manyToOneBinding, + manyToOneSource.getExplicitForeignKeyName() ); final var columnBinder = new ManyToOneColumnBinder( sourceDocument, @@ -1918,7 +1939,6 @@ private void bindManyToOneAttribute( manyToOneBinding, referencedEntityName ); - if ( canBindColumnsImmediately && fkSecondPass.canProcessImmediately() ) { fkSecondPass.doSecondPass( null ); } @@ -1927,7 +1947,19 @@ private void bindManyToOneAttribute( } } - manyToOneBinding.setOnDeleteAction( getOnDeleteAction( manyToOneSource.isCascadeDeleteEnabled() ) ); + manyToOneBinding.setOnDeleteAction( getOnDeleteAction( manyToOneSource ) ); + } + + private static void handleReferencedEntity( + ManyToOne manyToOneBinding, String referencedEntityName, String referencedEntityAttributeName) { + manyToOneBinding.setReferencedEntityName( referencedEntityName ); + if ( isNotEmpty( referencedEntityAttributeName ) ) { + manyToOneBinding.setReferencedPropertyName( referencedEntityAttributeName ); + manyToOneBinding.setReferenceToPrimaryKey( false ); + } + else { + manyToOneBinding.setReferenceToPrimaryKey( true ); + } } private static void setForeignKeyName(SimpleValue manyToOneBinding, String foreignKeyName) { @@ -1946,13 +1978,9 @@ private Property createAnyAssociationAttribute( SingularAttributeSourceAny anyMapping, Any anyBinding, String entityName) { - final var attributeRole = anyMapping.getAttributeRole(); - bindAny( sourceDocument, anyMapping, anyBinding, attributeRole ); - prepareValueTypeViaReflection( sourceDocument, anyBinding, entityName, anyMapping.getName(), attributeRole ); - anyBinding.createForeignKey(); final var property = new Property(); @@ -1999,9 +2027,7 @@ private void bindAny( anyMapping.getKeySource().getRelationalValueSources(), anyBinding.getKeyMapping(), true, - context -> implicitNamingStrategy.determineAnyKeyColumnName( - anyMapping.getKeySource() - ) + context -> implicitNamingStrategy.determineAnyKeyColumnName( anyMapping.getKeySource() ) ); } @@ -2111,11 +2137,8 @@ private BasicType resolveExplicitlyNamedAnyDiscriminatorType( } throw new org.hibernate.MappingException( - String.format( - Locale.ROOT, - "Unable to resolve explicit any-discriminator type name - %s", - typeName - ) + "Unable to resolve explicit any-discriminator type name:" + + typeName ); } } @@ -2141,7 +2164,8 @@ private void prepareValueTypeViaReflection( throw new MappingException( String.format( Locale.ENGLISH, - "Attribute mapping must define a name attribute: containingClassName=[%s], propertyName=[%s], role=[%s]", + "Attribute mapping must define a name attribute:" + + " containingClassName=[%s], propertyName=[%s], role=[%s]", containingClassName, propertyName, attributeRole.getFullPath() @@ -2157,7 +2181,8 @@ private void prepareValueTypeViaReflection( throw new MappingException( String.format( Locale.ENGLISH, - "Error calling Value#setTypeUsingReflection: containingClassName=[%s], propertyName=[%s], role=[%s]", + "Error calling Value#setTypeUsingReflection:" + + " containingClassName=[%s], propertyName=[%s], role=[%s]", containingClassName, propertyName, attributeRole.getFullPath() @@ -2194,13 +2219,15 @@ private void bindProperty( property.setUpdatable( singularAttributeSource.isUpdatable() ); // isBytecodeLazy() refers to whether a property is lazy via bytecode enhancement (not proxies) property.setLazy( singularAttributeSource.isBytecodeLazy() ); - handleGenerationTiming( mappingDocument, propertySource, property, singularAttributeSource.getGenerationTiming() ); + handleGenerationTiming( mappingDocument, propertySource, property, + singularAttributeSource.getGenerationTiming() ); } property.setMetaAttributes( propertySource.getToolingHintContext().getMetaAttributeMap() ); if ( BOOT_LOGGER.isTraceEnabled() ) { - BOOT_LOGGER.trace( "Mapped property: " + propertySource.getName() + " -> [" + columns( property.getValue() ) + "]" ); + BOOT_LOGGER.trace( "Mapped property: " + propertySource.getName() + + " -> [" + columns( property.getValue() ) + "]" ); } } @@ -2362,8 +2389,9 @@ private static void bindVirtual(String role, Component componentBinding) { // virtual (what used to be called embedded) is just a conceptual composition... // for example if ( componentBinding.getOwner().hasPojoRepresentation() ) { - BOOT_LOGGER.bindingVirtualComponentToOwner( role, componentBinding.getOwner().getClassName() ); - componentBinding.setComponentClassName( componentBinding.getOwner().getClassName() ); + final String ownerClassName = componentBinding.getOwner().getClassName(); + BOOT_LOGGER.bindingVirtualComponentToOwner( role, ownerClassName ); + componentBinding.setComponentClassName( ownerClassName ); } else { BOOT_LOGGER.bindingVirtualComponentAsDynamic( role ); @@ -2552,8 +2580,9 @@ private static TypeResolution resolveType( else { // the explicit name referred to a type-def typeName = typeDefinition.getTypeImplementorClass().getName(); - if ( typeDefinition.getParameters() != null ) { - typeParameters.putAll( typeDefinition.getParameters() ); + final var parameters = typeDefinition.getParameters(); + if ( parameters != null ) { + typeParameters.putAll( parameters ); } } @@ -2573,12 +2602,10 @@ private Table bindEntityTableSpecification( final EntitySource entitySource, PersistentClass entityDescriptor) { final String contributorName = mappingDocument.getCurrentContributorName(); - final boolean isTable = tableSpecSource instanceof TableSource; final boolean isAbstract = entityDescriptor.isAbstract() != null && entityDescriptor.isAbstract(); final Identifier logicalTableName; final Table table; - if ( isTable ) { - final var tableSource = (TableSource) tableSpecSource; + if ( tableSpecSource instanceof TableSource tableSource ) { logicalTableName = logicalTableName( mappingDocument, entitySource, tableSource ); table = tableForEntity( tableSource, @@ -2588,8 +2615,7 @@ private Table bindEntityTableSpecification( isAbstract ); } - else { - final var inlineViewSource = (InLineViewSource) tableSpecSource; + else if ( tableSpecSource instanceof InLineViewSource inlineViewSource ) { logicalTableName = database.toIdentifier( inlineViewSource.getLogicalName() ); table = tableForSubselect( inlineViewSource, @@ -2599,6 +2625,9 @@ private Table bindEntityTableSpecification( logicalTableName ); } + else { + throw new AssertionFailure( "Unexpected table specification source type" ); + } final var metadataCollector = mappingDocument.getMetadataCollector(); @@ -2609,8 +2638,7 @@ private Table bindEntityTableSpecification( superEntityTableXref( mappingDocument, entitySource, entityDescriptor, metadataCollector ) ); - if ( isTable ) { - final var tableSource = (TableSource) tableSpecSource; + if ( tableSpecSource instanceof TableSource tableSource ) { table.setRowId( tableSource.getRowId() ); final String checkConstraint = tableSource.getCheckConstraint(); if ( isNotEmpty( checkConstraint ) ) { @@ -2805,7 +2833,6 @@ private void registerSecondPass(SecondPass secondPass, MetadataBuildingContext c } - public static final class DelayedPropertyReferenceHandlerImpl implements InFlightMetadataCollector.DelayedPropertyReferenceHandler { public final String referencedEntityName; @@ -2947,9 +2974,7 @@ private void bindCollectionTable() { } else { final var tableSpecSource = pluralAttributeSource.getCollectionTableSpecificationSource(); - final Identifier logicalCatalogName = determineCatalogName( tableSpecSource ); - final Identifier logicalSchemaName = determineSchemaName( tableSpecSource ); - final var namespace = database.locateNamespace( logicalCatalogName, logicalSchemaName ); + final var namespace = locateTableNamespace( tableSpecSource ); final Table collectionTable; if ( tableSpecSource instanceof TableSource tableSource ) { @@ -2963,19 +2988,21 @@ private void bindCollectionTable() { ) ); } - else { + else if ( tableSpecSource instanceof InLineViewSource inlineViewSource ) { collectionTable = new Table( metadataBuildingContext.getCurrentContributorName(), namespace, - ( (InLineViewSource) tableSpecSource ).getSelectStatement(), + inlineViewSource.getSelectStatement(), false ); } + else { + throw new AssertionFailure( "Unexpected table specification source type" ); + } collectionBinding.setCollectionTable( collectionTable ); } - final var collectionTable = collectionBinding.getCollectionTable(); if ( BOOT_LOGGER.isTraceEnabled() ) { @@ -2984,36 +3011,33 @@ private void bindCollectionTable() { collectionTable.getName() ); } - if ( pluralAttributeSource.getCollectionTableComment() != null ) { - collectionTable.setComment( pluralAttributeSource.getCollectionTableComment() ); + final String tableComment = pluralAttributeSource.getCollectionTableComment(); + if ( tableComment != null ) { + collectionTable.setComment( tableComment ); } - if ( pluralAttributeSource.getCollectionTableCheck() != null ) { - collectionTable.addCheckConstraint( pluralAttributeSource.getCollectionTableCheck() ); + final String tableCheck = pluralAttributeSource.getCollectionTableCheck(); + if ( tableCheck != null ) { + collectionTable.addCheckConstraint( tableCheck ); } } private Identifier logicalName(TableSource tableSource) { - if ( isNotEmpty( tableSource.getExplicitTableName() ) ) { - return toIdentifier( tableSource.getExplicitTableName(), + final String explicitTableName = tableSource.getExplicitTableName(); + if ( isNotEmpty( explicitTableName ) ) { + return toIdentifier( explicitTableName, mappingDocument.getEffectiveDefaults().isDefaultQuoteIdentifiers() ); } else { - final var owner = collectionBinding.getOwner(); - final var ownerEntityNaming = new EntityNamingSourceImpl( - owner.getEntityName(), - owner.getClassName(), - owner.getJpaEntityName() - ); final var implicitNamingSource = new ImplicitCollectionTableNameSource() { @Override public Identifier getOwningPhysicalTableName() { - return owner.getTable().getNameIdentifier(); + return collectionBinding.getOwner().getTable().getNameIdentifier(); } @Override public EntityNaming getOwningEntityNaming() { - return ownerEntityNaming; + return new EntityNamingSourceImpl( collectionBinding.getOwner() ); } @Override @@ -3037,10 +3061,11 @@ protected void createBackReferences() { && !collectionBinding.getKey().isNullable() ) { // for non-inverse one-to-many, with a not-null fk, add a backref! final var oneToMany = (OneToMany) collectionBinding.getElement(); - final String entityName = oneToMany.getReferencedEntityName(); - final var referenced = getReferencedEntityBinding( entityName ); + final var referenced = + getReferencedEntityBinding( oneToMany.getReferencedEntityName() ); final var backref = new Backref(); - backref.setName( '_' + collectionBinding.getOwnerEntityName() + "." + pluralAttributeSource.getName() + "Backref" ); + backref.setName( '_' + collectionBinding.getOwnerEntityName() + + "." + pluralAttributeSource.getName() + "Backref" ); backref.setOptional( true ); backref.setUpdatable( false ); backref.setSelectable( false ); @@ -3075,8 +3100,9 @@ protected void bindCollectionKey() { collectionBinding.getCollectionTable(), keyVal ); - setForeignKeyName( key, keySource.getExplicitForeignKeyName() ); - key.setOnDeleteAction( getOnDeleteAction( pluralAttributeSource.getKeySource().isCascadeDeleteEnabled() ) ); + setForeignKeyName( key, + keySource.getExplicitForeignKeyName() ); + key.setOnDeleteAction( getOnDeleteAction( keySource ) ); // final ImplicitJoinColumnNameSource.Nature implicitNamingNature; // if ( getPluralAttributeSource().getElementSource() instanceof PluralAttributeElementSourceManyToMany @@ -3105,7 +3131,7 @@ protected void bindCollectionKey() { } protected void bindCollectionIdentifier() { - final CollectionIdSource idSource = getPluralAttributeSource().getCollectionIdSource(); + final var idSource = getPluralAttributeSource().getCollectionIdSource(); if ( idSource != null ) { final var idBagBinding = (IdentifierCollection) getCollectionBinding(); final var idBinding = new BasicValue( @@ -3142,158 +3168,106 @@ protected void bindCollectionIndex() { } protected void bindCollectionElement() { - final PluralAttributeElementSource pluralElementSource = getPluralAttributeSource().getElementSource(); + final var pluralElementSource = pluralAttributeSource.getElementSource(); if ( BOOT_LOGGER.isTraceEnabled() ) { - BOOT_LOGGER.tracef( - "Binding [%s] element type for a [%s]", - pluralElementSource.getNature(), - getPluralAttributeSource().getNature() - ); + BOOT_LOGGER.tracef( "Binding [%s] element type for a [%s]", + pluralElementSource.getNature(), pluralAttributeSource.getNature() ); } - final var collectionBinding = getCollectionBinding(); - final var mappingDocument = getMappingDocument(); if ( pluralElementSource instanceof PluralAttributeElementSourceBasic elementSource ) { - final var elementBinding = - new BasicValue( mappingDocument, collectionBinding.getCollectionTable() ); - - bindSimpleValueType( - mappingDocument, - elementSource.getExplicitHibernateTypeSource(), - elementBinding - ); - - relationalObjectBinder.bindColumnsAndFormulas( - this.mappingDocument, - elementSource.getRelationalValueSources(), - elementBinding, - elementSource.areValuesNullableByDefault(), - context -> context.getMetadataCollector().getDatabase() - .toIdentifier( Collection.DEFAULT_ELEMENT_COLUMN_NAME ) - ); - - collectionBinding.setElement( elementBinding ); - // Collection#setWhere is used to set the "where" clause that applies to the collection table - // (the table containing the basic elements) - // This "where" clause comes from the collection mapping; e.g., - collectionBinding.setWhere( getPluralAttributeSource().getWhere() ); + bindBasicElement( elementSource ); } else if ( pluralElementSource instanceof PluralAttributeElementSourceEmbedded elementSource ) { - final var elementBinding = new Component( mappingDocument, collectionBinding ); - - final var embeddableSource = elementSource.getEmbeddableSource(); - bindComponent( - this.mappingDocument, - embeddableSource, - elementBinding, - null, - embeddableSource.getAttributePathBase().getProperty(), - false - ); - - collectionBinding.setElement( elementBinding ); - // Collection#setWhere is used to set the "where" clause that applies to the collection table - // (the table containing the embeddable elements) - // This "where" clause comes from the collection mapping; e.g., - collectionBinding.setWhere( getPluralAttributeSource().getWhere() ); + bindEmbeddedElement( elementSource ); } else if ( pluralElementSource instanceof PluralAttributeElementSourceOneToMany elementSource ) { - final var elementBinding = - new OneToMany( mappingDocument, collectionBinding.getOwner() ); - this.collectionBinding.setElement( elementBinding ); - - final var referencedEntityBinding = - getReferencedEntityBinding( elementSource.getReferencedEntityName() ); - - this.collectionBinding.setWhere( - getNonEmptyOrConjunctionIfBothNonEmpty( - referencedEntityBinding.getWhere(), - getPluralAttributeSource().getWhere() - ) - ); - - elementBinding.setReferencedEntityName( referencedEntityBinding.getEntityName() ); - elementBinding.setAssociatedClass( referencedEntityBinding ); - elementBinding.setIgnoreNotFound( elementSource.isIgnoreNotFound() ); + bindOneToManyElement( elementSource ); } else if ( pluralElementSource instanceof PluralAttributeElementSourceManyToMany elementSource ) { - final var elementBinding = - new ManyToOne( mappingDocument, collectionBinding.getCollectionTable() ); - - relationalObjectBinder.bindColumnsAndFormulas( - mappingDocument, - elementSource.getRelationalValueSources(), - elementBinding, - false, - context -> context.getMetadataCollector().getDatabase() - .toIdentifier( Collection.DEFAULT_ELEMENT_COLUMN_NAME ) - ); + bindManyToManyElement( elementSource ); + } + else if ( pluralElementSource instanceof PluralAttributeElementSourceManyToAny elementSource ) { + bindManyToAnyElement( elementSource ); + } + } - final var fetchCharacteristics = elementSource.getFetchCharacteristics(); - elementBinding.setLazy( fetchCharacteristics.getFetchTiming() != FetchTiming.IMMEDIATE ); - elementBinding.setFetchMode( - fetchCharacteristics.getFetchStyle() == FetchStyle.SELECT - ? FetchMode.SELECT - : FetchMode.JOIN - ); + private void bindManyToAnyElement(PluralAttributeElementSourceManyToAny elementSource) { + final var elementBinding = + new Any( mappingDocument, + collectionBinding.getCollectionTable() ); + bindAny( mappingDocument, elementSource, elementBinding, + pluralAttributeSource.getAttributeRole().append( "element" ) ); + collectionBinding.setElement( elementBinding ); + // Collection#setWhere is used to set the "where" clause that applies to the collection table + // (which is the join table for a many-to-any association). + // This "where" clause comes from the collection mapping; e.g., + collectionBinding.setWhere( getPluralAttributeSource().getWhere() ); + } - setForeignKeyName( elementBinding, elementSource.getExplicitForeignKeyName() ); + private void bindManyToManyElement(PluralAttributeElementSourceManyToMany elementSource) { + final var elementBinding = + new ManyToOne( mappingDocument, + collectionBinding.getCollectionTable() ); + relationalObjectBinder.bindColumnsAndFormulas( + mappingDocument, + elementSource.getRelationalValueSources(), + elementBinding, + false, + context -> context.getMetadataCollector().getDatabase() + .toIdentifier( Collection.DEFAULT_ELEMENT_COLUMN_NAME ) + ); + handleFetchCharacteristics( elementSource, elementBinding ); + setForeignKeyName( elementBinding, + elementSource.getExplicitForeignKeyName() ); + final String referencedEntityName = elementSource.getReferencedEntityName(); + handleReferencedEntity( elementBinding, referencedEntityName, + elementSource.getReferencedEntityAttributeName() ); + collectionBinding.setElement( elementBinding ); + final var referencedEntityBinding = getReferencedEntityBinding( referencedEntityName ); + // Collection#setWhere is used to set the "where" clause that applies to the collection table + // (which is the join table for a many-to-many association). + // This "where" clause comes from the collection mapping; e.g., + collectionBinding.setWhere( pluralAttributeSource.getWhere() ); + collectionBinding.setManyToManyWhere( + getNonEmptyOrConjunctionIfBothNonEmpty( + referencedEntityBinding.getWhere(), + elementSource.getWhere() + ) + ); + collectionBinding.setManyToManyOrdering( elementSource.getOrder() ); + bindManyToManyFilters( elementSource, mappingDocument, collectionBinding, elementBinding ); + } - final String referencedEntityName = elementSource.getReferencedEntityName(); - elementBinding.setReferencedEntityName( referencedEntityName ); - final String referencedEntityAttributeName = elementSource.getReferencedEntityAttributeName(); - if ( isNotEmpty( referencedEntityAttributeName ) ) { - elementBinding.setReferencedPropertyName( referencedEntityAttributeName ); - elementBinding.setReferenceToPrimaryKey( false ); - } - else { - elementBinding.setReferenceToPrimaryKey( true ); + private void bindManyToManyFilters( + PluralAttributeElementSourceManyToMany elementSource, + MappingDocument mappingDocument, + Collection collectionBinding, + ManyToOne elementBinding) { + if ( !isEmpty( elementSource.getFilterSources() ) + || elementSource.getWhere() != null ) { + if ( collectionBinding.getFetchMode() == FetchMode.JOIN + && elementBinding.getFetchMode() != FetchMode.JOIN ) { + throw new MappingException( + String.format( + Locale.ENGLISH, + "many-to-many defining filter or where without join fetching is not " + + "valid within collection [%s] using join fetching", + getPluralAttributeSource().getAttributeRole().getFullPath() + ), + mappingDocument.getOrigin() + ); } + } - collectionBinding.setElement( elementBinding ); - - final PersistentClass referencedEntityBinding = getReferencedEntityBinding( referencedEntityName ); - - // Collection#setWhere is used to set the "where" clause that applies to the collection table - // (which is the join table for a many-to-many association). - // This "where" clause comes from the collection mapping; e.g., - collectionBinding.setWhere( getPluralAttributeSource().getWhere() ); - - collectionBinding.setManyToManyWhere( - getNonEmptyOrConjunctionIfBothNonEmpty( - referencedEntityBinding.getWhere(), - elementSource.getWhere() - ) - ); - - collectionBinding.setManyToManyOrdering( elementSource.getOrder() ); - - if ( !isEmpty( elementSource.getFilterSources() ) - || elementSource.getWhere() != null ) { - if ( collectionBinding.getFetchMode() == FetchMode.JOIN - && elementBinding.getFetchMode() != FetchMode.JOIN ) { - throw new MappingException( - String.format( - Locale.ENGLISH, - "many-to-many defining filter or where without join fetching is not " + - "valid within collection [%s] using join fetching", - getPluralAttributeSource().getAttributeRole().getFullPath() - ), - mappingDocument.getOrigin() + for ( var filterSource : elementSource.getFilterSources() ) { + if ( filterSource.getName() == null ) { + if ( BOOT_LOGGER.isTraceEnabled() ) { + BOOT_LOGGER.tracef( + "Encountered filter with no name associated with many-to-many [%s]; skipping", + getPluralAttributeSource().getAttributeRole().getFullPath() ); } } - - for ( var filterSource : elementSource.getFilterSources() ) { - if ( filterSource.getName() == null ) { - if ( BOOT_LOGGER.isTraceEnabled() ) { - BOOT_LOGGER.tracef( - "Encountered filter with no name associated with many-to-many [%s]; skipping", - getPluralAttributeSource().getAttributeRole().getFullPath() - ); - } - continue; - } - + else { if ( filterSource.getCondition() == null ) { throw new MappingException( String.format( @@ -3305,7 +3279,6 @@ else if ( pluralElementSource instanceof PluralAttributeElementSourceManyToMany mappingDocument.getOrigin() ); } - if ( BOOT_LOGGER.isTraceEnabled() ) { BOOT_LOGGER.tracef( "Applying many-to-many filter [%s] as [%s] to collection [%s]", @@ -3314,7 +3287,6 @@ else if ( pluralElementSource instanceof PluralAttributeElementSourceManyToMany getPluralAttributeSource().getAttributeRole().getFullPath() ); } - collectionBinding.addManyToManyFilter( filterSource.getName(), filterSource.getCondition(), @@ -3324,20 +3296,67 @@ else if ( pluralElementSource instanceof PluralAttributeElementSourceManyToMany ); } } - else if ( pluralElementSource instanceof PluralAttributeElementSourceManyToAny elementSource ) { - final var elementBinding = new Any( mappingDocument, collectionBinding.getCollectionTable() ); - bindAny( - this.mappingDocument, - elementSource, - elementBinding, - getPluralAttributeSource().getAttributeRole().append( "element" ) - ); - collectionBinding.setElement( elementBinding ); - // Collection#setWhere is used to set the "where" clause that applies to the collection table - // (which is the join table for a many-to-any association). - // This "where" clause comes from the collection mapping; e.g., - collectionBinding.setWhere( getPluralAttributeSource().getWhere() ); - } + } + + private void bindOneToManyElement(PluralAttributeElementSourceOneToMany elementSource) { + final var elementBinding = + new OneToMany( mappingDocument, + collectionBinding.getOwner() ); + collectionBinding.setElement( elementBinding ); + final var referencedEntityBinding = + getReferencedEntityBinding( elementSource.getReferencedEntityName() ); + collectionBinding.setWhere( + getNonEmptyOrConjunctionIfBothNonEmpty( + referencedEntityBinding.getWhere(), + pluralAttributeSource.getWhere() + ) + ); + elementBinding.setReferencedEntityName( referencedEntityBinding.getEntityName() ); + elementBinding.setAssociatedClass( referencedEntityBinding ); + elementBinding.setIgnoreNotFound( elementSource.isIgnoreNotFound() ); + } + + private void bindEmbeddedElement(PluralAttributeElementSourceEmbedded elementSource) { + final var elementBinding = new Component( mappingDocument, collectionBinding ); + final var embeddableSource = elementSource.getEmbeddableSource(); + bindComponent( mappingDocument, embeddableSource, elementBinding, null, + embeddableSource.getAttributePathBase().getProperty(), false ); + collectionBinding.setElement( elementBinding ); + // Collection#setWhere is used to set the "where" clause that applies to the collection table + // (the table containing the embeddable elements) + // This "where" clause comes from the collection mapping; e.g., + collectionBinding.setWhere( pluralAttributeSource.getWhere() ); + } + + private void bindBasicElement(PluralAttributeElementSourceBasic elementSource) { + final var elementBinding = + new BasicValue( mappingDocument, + collectionBinding.getCollectionTable() ); + bindSimpleValueType( mappingDocument, elementSource.getExplicitHibernateTypeSource(), elementBinding ); + relationalObjectBinder.bindColumnsAndFormulas( + mappingDocument, + elementSource.getRelationalValueSources(), + elementBinding, + elementSource.areValuesNullableByDefault(), + context -> context.getMetadataCollector().getDatabase() + .toIdentifier( Collection.DEFAULT_ELEMENT_COLUMN_NAME ) + ); + collectionBinding.setElement( elementBinding ); + // Collection#setWhere is used to set the "where" clause that applies to the collection table + // (the table containing the basic elements) + // This "where" clause comes from the collection mapping; e.g., + collectionBinding.setWhere( pluralAttributeSource.getWhere() ); + } + + private static void handleFetchCharacteristics( + PluralAttributeElementSourceManyToMany elementSource, ManyToOne elementBinding) { + final var characteristics = elementSource.getFetchCharacteristics(); + elementBinding.setLazy( characteristics.getFetchTiming() != FetchTiming.IMMEDIATE ); + elementBinding.setFetchMode( + characteristics.getFetchStyle() == FetchStyle.SELECT + ? FetchMode.SELECT + : FetchMode.JOIN + ); } private PersistentClass getReferencedEntityBinding(String referencedEntityName) { @@ -3352,7 +3371,7 @@ private PersistentClass getReferencedEntityBinding(String referencedEntityName) getPluralAttributeSource().getAttributeRole().getFullPath(), referencedEntityName ), - getMappingDocument().getOrigin() + mappingDocument.getOrigin() ); } return entityBinding; @@ -3453,9 +3472,12 @@ protected void createBackReferences() { && !indexIsFormula ) { final var oneToMany = (OneToMany) collectionBinding.getElement(); final String entityName = oneToMany.getReferencedEntityName(); - final var referenced = getMappingDocument().getMetadataCollector().getEntityBinding( entityName ); + final var referenced = + getMappingDocument().getMetadataCollector() + .getEntityBinding( entityName ); final var backref = new IndexBackref(); - backref.setName( '_' + collectionBinding.getOwnerEntityName() + "." + getPluralAttributeSource().getName() + "IndexBackref" ); + backref.setName( '_' + collectionBinding.getOwnerEntityName() + + "." + getPluralAttributeSource().getName() + "IndexBackref" ); backref.setOptional( true ); backref.setUpdatable( false ); backref.setSelectable( false ); @@ -3532,9 +3554,12 @@ private void createIndexBackRef( && !collectionBinding.isInverse() ) { final var oneToMany = (OneToMany) collectionBinding.getElement(); final String entityName = oneToMany.getReferencedEntityName(); - final var referenced = mappingDocument.getMetadataCollector().getEntityBinding( entityName ); + final var referenced = + mappingDocument.getMetadataCollector() + .getEntityBinding( entityName ); final var backref = new IndexBackref(); - backref.setName( '_' + collectionBinding.getOwnerEntityName() + "." + pluralAttributeSource.getName() + "IndexBackref" ); + backref.setName( '_' + collectionBinding.getOwnerEntityName() + + "." + pluralAttributeSource.getName() + "IndexBackref" ); backref.setOptional( true ); backref.setUpdatable( false ); backref.setSelectable( false ); @@ -3575,7 +3600,6 @@ protected void bindCollectionIndex() { @Override protected void createBackReferences() { super.createBackReferences(); - createIndexBackRef( getMappingDocument(), getPluralAttributeSource(), @@ -3591,7 +3615,9 @@ public void bindListOrArrayIndex( final var indexSource = (PluralAttributeSequentialIndexSource) attributeSource.getIndexSource(); - final var indexBinding = new BasicValue( mappingDocument, collectionBinding.getCollectionTable() ); + final var indexBinding = + new BasicValue( mappingDocument, + collectionBinding.getCollectionTable() ); bindSimpleValueType( mappingDocument, indexSource.getTypeInformation(), indexBinding ); relationalObjectBinder.bindColumnsAndFormulas( @@ -3624,70 +3650,108 @@ private void bindMapKey( final org.hibernate.mapping.Map collectionBinding) { final var indexSource = pluralAttributeSource.getIndexSource(); if ( indexSource instanceof PluralAttributeMapKeySourceBasic mapKeySource ) { - final var value = new BasicValue( mappingDocument, collectionBinding.getCollectionTable() ); - bindSimpleValueType( mappingDocument, mapKeySource.getTypeInformation(), value ); - if ( !value.isTypeSpecified() ) { - throw new MappingException( - "map index element must specify a type: " - + pluralAttributeSource.getAttributeRole().getFullPath(), - mappingDocument.getOrigin() - ); - } - - relationalObjectBinder.bindColumnsAndFormulas( - mappingDocument, - mapKeySource.getRelationalValueSources(), - value, - true, - context -> database.toIdentifier( IndexedCollection.DEFAULT_INDEX_COLUMN_NAME ) - ); - - collectionBinding.setIndex( value ); + bindBasicMapKey( mappingDocument, pluralAttributeSource, collectionBinding, mapKeySource ); } else if ( indexSource instanceof PluralAttributeMapKeySourceEmbedded mapKeySource ) { - final var componentBinding = new Component( mappingDocument, collectionBinding ); - bindComponent( - mappingDocument, - mapKeySource.getEmbeddableSource(), - componentBinding, - null, - pluralAttributeSource.getName(), - false - ); - collectionBinding.setIndex( componentBinding ); + bindEmbeddedMapKey( mappingDocument, pluralAttributeSource, collectionBinding, mapKeySource ); } else if ( indexSource instanceof PluralAttributeMapKeyManyToManySource mapKeySource ) { - final var mapKeyBinding = new ManyToOne( mappingDocument, collectionBinding.getCollectionTable() ); + bindManyToManyMapKey( mappingDocument, pluralAttributeSource, collectionBinding, mapKeySource ); + } + else if ( indexSource instanceof PluralAttributeMapKeyManyToAnySource mapKeySource) { + bindManyToAnyMapKey( mappingDocument, pluralAttributeSource, collectionBinding, mapKeySource ); + } + } - mapKeyBinding.setReferencedEntityName( mapKeySource.getReferencedEntityName() ); + private void bindManyToAnyMapKey( + MappingDocument mappingDocument, + IndexedPluralAttributeSource pluralAttributeSource, + org.hibernate.mapping.Map collectionBinding, + PluralAttributeMapKeyManyToAnySource mapKeySource) { + final var mapKeyBinding = + new Any( mappingDocument, + collectionBinding.getCollectionTable() ); + bindAny( mappingDocument, mapKeySource, mapKeyBinding, + pluralAttributeSource.getAttributeRole().append( "key" ) ); + collectionBinding.setIndex( mapKeyBinding ); + } - relationalObjectBinder.bindColumnsAndFormulas( - mappingDocument, - mapKeySource.getRelationalValueSources(), - mapKeyBinding, - true, - context -> implicitNamingStrategy.determineMapKeyColumnName( - new ImplicitMapKeyColumnNameSource() { - @Override - public AttributePath getPluralAttributePath() { - return pluralAttributeSource.getAttributePath(); - } + private void bindManyToManyMapKey( + MappingDocument mappingDocument, + IndexedPluralAttributeSource pluralAttributeSource, + org.hibernate.mapping.Map collectionBinding, + PluralAttributeMapKeyManyToManySource mapKeySource) { + final var mapKeyBinding = + new ManyToOne( mappingDocument, + collectionBinding.getCollectionTable() ); - @Override - public MetadataBuildingContext getBuildingContext() { - return context; - } + mapKeyBinding.setReferencedEntityName( mapKeySource.getReferencedEntityName() ); + + relationalObjectBinder.bindColumnsAndFormulas( + mappingDocument, + mapKeySource.getRelationalValueSources(), + mapKeyBinding, + true, + context -> implicitNamingStrategy.determineMapKeyColumnName( + new ImplicitMapKeyColumnNameSource() { + @Override + public AttributePath getPluralAttributePath() { + return pluralAttributeSource.getAttributePath(); } - ) + + @Override + public MetadataBuildingContext getBuildingContext() { + return context; + } + } + ) + ); + collectionBinding.setIndex( mapKeyBinding ); + } + + private void bindEmbeddedMapKey( + MappingDocument mappingDocument, + IndexedPluralAttributeSource pluralAttributeSource, + org.hibernate.mapping.Map collectionBinding, + PluralAttributeMapKeySourceEmbedded mapKeySource) { + final var componentBinding = new Component( mappingDocument, collectionBinding ); + bindComponent( + mappingDocument, + mapKeySource.getEmbeddableSource(), + componentBinding, + null, + pluralAttributeSource.getName(), + false + ); + collectionBinding.setIndex( componentBinding ); + } + + private void bindBasicMapKey( + MappingDocument mappingDocument, + IndexedPluralAttributeSource pluralAttributeSource, + org.hibernate.mapping.Map collectionBinding, + PluralAttributeMapKeySourceBasic mapKeySource) { + final var value = + new BasicValue( mappingDocument, + collectionBinding.getCollectionTable() ); + bindSimpleValueType( mappingDocument, mapKeySource.getTypeInformation(), value ); + if ( !value.isTypeSpecified() ) { + throw new MappingException( + "map index element must specify a type: " + + pluralAttributeSource.getAttributeRole().getFullPath(), + mappingDocument.getOrigin() ); - collectionBinding.setIndex( mapKeyBinding ); - } - else if ( indexSource instanceof PluralAttributeMapKeyManyToAnySource mapKeySource) { - final var mapKeyBinding = new Any( mappingDocument, collectionBinding.getCollectionTable() ); - bindAny( mappingDocument, mapKeySource, mapKeyBinding, - pluralAttributeSource.getAttributeRole().append( "key" ) ); - collectionBinding.setIndex( mapKeyBinding ); } + + relationalObjectBinder.bindColumnsAndFormulas( + mappingDocument, + mapKeySource.getRelationalValueSources(), + value, + true, + context -> database.toIdentifier( IndexedCollection.DEFAULT_INDEX_COLUMN_NAME ) + ); + + collectionBinding.setIndex( value ); } private class ManyToOneColumnBinder implements ImplicitColumnNamingSecondPass { @@ -3727,7 +3791,8 @@ public boolean canProcessImmediately() { } final var referencedEntityBinding = - mappingDocument.getMetadataCollector().getEntityBinding( referencedEntityName ); + mappingDocument.getMetadataCollector() + .getEntityBinding( referencedEntityName ); if ( referencedEntityBinding == null ) { return false; } @@ -3759,7 +3824,8 @@ public void doSecondPass(Map persistentClasses) { // column making up the FK. final var referencedEntityBinding = - mappingDocument.getMetadataCollector().getEntityBinding( referencedEntityName ); + mappingDocument.getMetadataCollector() + .getEntityBinding( referencedEntityName ); if ( referencedEntityBinding == null ) { throw new AssertionFailure( @@ -3810,7 +3876,8 @@ private ManyToOneFkSecondPass( String referencedEntityName) { if ( referencedEntityName == null ) { throw new MappingException( - "entity name referenced by many-to-one required [" + manyToOneSource.getAttributeRole().getFullPath() + "]", + "entity name referenced by many-to-one required [" + + manyToOneSource.getAttributeRole().getFullPath() + "]", mappingDocument.getOrigin() ); } @@ -3841,7 +3908,9 @@ public void doSecondPass(Map persistentClasses) throws manyToOneBinding.createForeignKey(); } else { - manyToOneBinding.createPropertyRefConstraints( mappingDocument.getMetadataCollector().getEntityBindingMap() ); + manyToOneBinding.createPropertyRefConstraints( + mappingDocument.getMetadataCollector() + .getEntityBindingMap() ); } } @@ -3945,10 +4014,14 @@ public Identifier getUserProvidedIdentifier() { } } ); - uniqueKey.setName( uniqueKeyName.render( mappingDocument.getMetadataCollector().getDatabase().getDialect() ) ); + uniqueKey.setName( uniqueKeyName.render( getDialect() ) ); entityBinding.getTable().addUniqueKey( uniqueKey ); } + + private Dialect getDialect() { + return mappingDocument.getMetadataCollector().getDatabase().getDialect(); + } } private String columns(Value value) { @@ -3962,7 +4035,7 @@ private String columns(Value value) { return builder.toString(); } - private static OnDeleteAction getOnDeleteAction(boolean entitySource) { - return entitySource ? OnDeleteAction.CASCADE : OnDeleteAction.NO_ACTION; + private static OnDeleteAction getOnDeleteAction(ForeignKeyContributingSource entitySource) { + return entitySource.isCascadeDeleteEnabled() ? OnDeleteAction.CASCADE : OnDeleteAction.NO_ACTION; } } From 6eba60e68ff00d1ebea2f6ab3ae76e09261b8125 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 26 Nov 2025 14:28:43 +0100 Subject: [PATCH 003/181] even more refactoring to ModelBinder --- .../source/internal/hbm/ModelBinder.java | 138 ++++++++---------- 1 file changed, 59 insertions(+), 79 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java index 9cdaa0178438..7194d44cf498 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java @@ -1059,11 +1059,9 @@ private void handleNaturalIdBinding( NaturalIdMutability naturalIdMutability) { if ( naturalIdMutability != NaturalIdMutability.NOT_NATURAL_ID ) { attributeBinding.setNaturalIdentifier( true ); - if ( naturalIdMutability == NaturalIdMutability.IMMUTABLE ) { attributeBinding.setUpdatable( false ); } - final var metadataCollector = mappingDocument.getMetadataCollector(); final String entityName = entityBinding.getEntityName(); var ukBinder = metadataCollector.locateNaturalIdUniqueKeyBinder( entityName ); @@ -1071,7 +1069,6 @@ private void handleNaturalIdBinding( ukBinder = new NaturalIdUniqueKeyBinderImpl( mappingDocument, entityBinding ); metadataCollector.registerNaturalIdUniqueKeyBinder( entityName, ukBinder ); } - ukBinder.addAttributeBinding( attributeBinding ); } } @@ -1081,12 +1078,10 @@ private Property createPluralAttribute( PluralAttributeSource attributeSource, PersistentClass entityDescriptor) { final Collection collectionBinding; - if ( attributeSource instanceof PluralAttributeSourceListImpl pluralAttributeSourceList ) { final var list = new org.hibernate.mapping.List(sourceDocument, entityDescriptor); collectionBinding = list; bindCollectionMetadata( sourceDocument, attributeSource, collectionBinding ); - registerSecondPass( new PluralAttributeListSecondPass( sourceDocument, pluralAttributeSourceList, list ), sourceDocument @@ -1095,7 +1090,6 @@ private Property createPluralAttribute( else if ( attributeSource instanceof PluralAttributeSourceSetImpl ) { collectionBinding = new Set( sourceDocument, entityDescriptor ); bindCollectionMetadata( sourceDocument, attributeSource, collectionBinding ); - registerSecondPass( new PluralAttributeSetSecondPass( sourceDocument, attributeSource, collectionBinding ), sourceDocument @@ -1105,7 +1099,6 @@ else if ( attributeSource instanceof PluralAttributeSourceMapImpl pluralAttribut final var map = new org.hibernate.mapping.Map( sourceDocument, entityDescriptor ); collectionBinding = map; bindCollectionMetadata( sourceDocument, attributeSource, collectionBinding ); - registerSecondPass( new PluralAttributeMapSecondPass( sourceDocument, pluralAttributeSourceMap, map ), sourceDocument @@ -1114,7 +1107,6 @@ else if ( attributeSource instanceof PluralAttributeSourceMapImpl pluralAttribut else if ( attributeSource instanceof PluralAttributeSourceBagImpl ) { collectionBinding = new Bag( sourceDocument, entityDescriptor ); bindCollectionMetadata( sourceDocument, attributeSource, collectionBinding ); - registerSecondPass( new PluralAttributeBagSecondPass( sourceDocument, attributeSource, collectionBinding ), sourceDocument @@ -1123,7 +1115,6 @@ else if ( attributeSource instanceof PluralAttributeSourceBagImpl ) { else if ( attributeSource instanceof PluralAttributeSourceIdBagImpl ) { collectionBinding = new IdentifierBag( sourceDocument, entityDescriptor ); bindCollectionMetadata( sourceDocument, attributeSource, collectionBinding ); - registerSecondPass( new PluralAttributeIdBagSecondPass( sourceDocument, attributeSource, collectionBinding ), sourceDocument @@ -1133,9 +1124,7 @@ else if ( attributeSource instanceof PluralAttributeSourceArrayImpl arraySource final var array = new Array(sourceDocument, entityDescriptor); collectionBinding = array; bindCollectionMetadata( sourceDocument, attributeSource, collectionBinding ); - array.setElementClassName( sourceDocument.qualifyClassName( arraySource.getElementClass() ) ); - registerSecondPass( new PluralAttributeArraySecondPass( sourceDocument, arraySource, array ), sourceDocument @@ -1145,7 +1134,6 @@ else if ( attributeSource instanceof PluralAttributeSourcePrimitiveArrayImpl plu final var primitiveArray = new PrimitiveArray( sourceDocument, entityDescriptor ); collectionBinding = primitiveArray; bindCollectionMetadata( sourceDocument, attributeSource, collectionBinding ); - registerSecondPass( new PluralAttributePrimitiveArraySecondPass( sourceDocument, @@ -1161,16 +1149,17 @@ else if ( attributeSource instanceof PluralAttributeSourcePrimitiveArrayImpl plu ); } - sourceDocument.getMetadataCollector().addCollectionBinding( collectionBinding ); + sourceDocument.getMetadataCollector() + .addCollectionBinding( collectionBinding ); final var attribute = new Property(); attribute.setValue( collectionBinding ); bindProperty( sourceDocument, attributeSource, attribute ); - return attribute; } - private void bindCollectionMetadata(MappingDocument mappingDocument, PluralAttributeSource source, Collection binding) { + private void bindCollectionMetadata( + MappingDocument mappingDocument, PluralAttributeSource source, Collection binding) { binding.setRole( source.getAttributeRole().getFullPath() ); binding.setInverse( source.isInverse() ); binding.setMutable( source.isMutable() ); @@ -1186,40 +1175,11 @@ private void bindCollectionMetadata(MappingDocument mappingDocument, PluralAttri applyCaching( mappingDocument, source.getCaching(), binding ); - // bind the collection type info - String typeName = source.getTypeInformation().getName(); - final Map typeParameters = new HashMap<>(); - if ( typeName != null ) { - // see if there is a corresponding type-def - final var typeDefinition = - mappingDocument.getMetadataCollector().getTypeDefinition( typeName ); - if ( typeDefinition != null ) { - typeName = typeDefinition.getTypeImplementorClass().getName(); - if ( typeDefinition.getParameters() != null ) { - typeParameters.putAll( typeDefinition.getParameters() ); - } - } - else { - // it could be an unqualified class name, in which case we should qualify - // it with the implicit package name for this context, if one. - typeName = mappingDocument.qualifyClassName( typeName ); - } - } - if ( source.getTypeInformation().getParameters() != null ) { - typeParameters.putAll( source.getTypeInformation().getParameters() ); - } - - binding.setTypeName( typeName ); - binding.setTypeParameters( typeParameters ); + bindCollectionType( mappingDocument, source, binding ); final var fetchCharacteristics = source.getFetchCharacteristics(); - if ( fetchCharacteristics.getFetchTiming() == FetchTiming.DELAYED ) { - binding.setLazy( true ); - binding.setExtraLazy( fetchCharacteristics.isExtraLazy() ); - } - else { - binding.setLazy( false ); - } + binding.setLazy( fetchCharacteristics.getFetchTiming() == FetchTiming.DELAYED ); + binding.setExtraLazy( fetchCharacteristics.isExtraLazy() ); setupFetching( source, binding ); @@ -1230,23 +1190,16 @@ private void bindCollectionMetadata(MappingDocument mappingDocument, PluralAttri binding.setLoaderName( source.getCustomLoaderName() ); bindCustomSql( source, binding ); - if ( source instanceof Sortable sortable ) { - if ( sortable.isSorted() ) { - binding.setSorted( true ); - final String comparatorName = sortable.getComparatorName(); - if ( !comparatorName.equals( "natural" ) ) { - binding.setComparatorClassName( comparatorName ); - } - } - else { - binding.setSorted( false ); + if ( source instanceof Sortable sortable && sortable.isSorted() ) { + binding.setSorted( true ); + final String comparatorName = sortable.getComparatorName(); + if ( !comparatorName.equals( "natural" ) ) { + binding.setComparatorClassName( comparatorName ); } } - if ( source instanceof Orderable orderable ) { - if ( orderable.isOrdered() ) { - binding.setOrderBy( orderable.getOrder() ); - } + if ( source instanceof Orderable orderable && orderable.isOrdered() ) { + binding.setOrderBy( orderable.getOrder() ); } final String cascadeStyle = source.getCascadeStyleName(); @@ -1265,6 +1218,41 @@ private void bindCollectionMetadata(MappingDocument mappingDocument, PluralAttri } } + private static void bindCollectionType( + MappingDocument mappingDocument, PluralAttributeSource source, Collection binding) { + // bind the collection type info + final String explicitTypeName = source.getTypeInformation().getName(); + final Map typeParameters = new HashMap<>(); + final String typeName; + if ( explicitTypeName != null ) { + // see if there is a corresponding type-def + final var typeDefinition = + mappingDocument.getMetadataCollector() + .getTypeDefinition( explicitTypeName ); + if ( typeDefinition != null ) { + typeName = typeDefinition.getTypeImplementorClass().getName(); + final var parameters = typeDefinition.getParameters(); + if ( parameters != null ) { + typeParameters.putAll( parameters ); + } + } + else { + // it could be an unqualified class name, in which case qualify + // it with the implicit package name for this context, if one. + typeName = mappingDocument.qualifyClassName( explicitTypeName ); + } + } + else { + typeName = null; + } + final var parameters = source.getTypeInformation().getParameters(); + if ( parameters != null ) { + typeParameters.putAll( parameters ); + } + binding.setTypeName( typeName ); + binding.setTypeParameters( typeParameters ); + } + private static void bindCustomSql(PluralAttributeSource source, Collection binding) { if ( source.getCustomSqlInsert() != null ) { binding.setCustomSQLInsert( @@ -1298,26 +1286,19 @@ private static void bindCustomSql(PluralAttributeSource source, Collection bindi private static void setupFetching(PluralAttributeSource source, Collection binding) { final var fetchCharacteristics = source.getFetchCharacteristics(); - switch ( fetchCharacteristics.getFetchStyle() ) { - case SELECT: - binding.setFetchMode( FetchMode.SELECT ); - break; - case JOIN: - binding.setFetchMode( FetchMode.JOIN ); - break; + final var fetchStyle = fetchCharacteristics.getFetchStyle(); + binding.setFetchMode( switch ( fetchStyle ) { + case SELECT, BATCH, SUBSELECT -> FetchMode.SELECT; + case JOIN -> FetchMode.JOIN; + } ); + switch ( fetchStyle ) { case BATCH: - binding.setFetchMode( FetchMode.SELECT ); binding.setBatchSize( fetchCharacteristics.getBatchSize() ); break; case SUBSELECT: - binding.setFetchMode( FetchMode.SELECT ); binding.setSubselectLoadable( true ); - // todo : this could totally be done using a "symbol map" approach binding.getOwner().setSubselectLoadableCollections( true ); break; - default: - throw new AssertionFailure( "Unexpected FetchStyle : " - + fetchCharacteristics.getFetchStyle().name() ); } } @@ -2565,17 +2546,18 @@ public TypeResolution(String typeName, Map parameters) { private static TypeResolution resolveType( MappingDocument sourceDocument, HibernateTypeSource typeSource) { - if ( StringHelper.isEmpty( typeSource.getName() ) ) { + final String typeSourceName = typeSource.getName(); + if ( StringHelper.isEmpty( typeSourceName ) ) { return null; } final var typeDefinition = sourceDocument.getMetadataCollector() - .getTypeDefinition( typeSource.getName() ); + .getTypeDefinition( typeSourceName ); final Map typeParameters = new HashMap<>(); final String typeName; if ( typeDefinition == null ) { - typeName = typeSource.getName(); + typeName = typeSourceName; } else { // the explicit name referred to a type-def @@ -2585,13 +2567,11 @@ private static TypeResolution resolveType( typeParameters.putAll( parameters ); } } - // parameters on the property mapping should override parameters in the type-def final var parameters = typeSource.getParameters(); if ( parameters != null ) { typeParameters.putAll( parameters ); } - return new TypeResolution( typeName, typeParameters ); } From b78f80fee466cc50fe2a86a1b6b8a6b9579fa931 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Mon, 6 Oct 2025 17:21:10 +0200 Subject: [PATCH 004/181] HHH-19843 Get persister from session factory if session is not available --- .../BeanValidationEventListener.java | 24 +++- ...lectionActionsValidationStatelessTest.java | 109 ++++++++++++++++++ 2 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/beanvalidation/CollectionActionsValidationStatelessTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationEventListener.java b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationEventListener.java index 04bf58a0e805..42735bf68f4d 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationEventListener.java @@ -13,6 +13,7 @@ import org.hibernate.boot.internal.ClassLoaderAccessImpl; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.event.spi.PreCollectionUpdateEvent; import org.hibernate.event.spi.PreCollectionUpdateEventListener; import org.hibernate.event.spi.PreDeleteEvent; @@ -51,8 +52,9 @@ public class BeanValidationEventListener private final Validator validator; private final GroupsPerOperation groupsPerOperation; - public BeanValidationEventListener( - ValidatorFactory factory, Map settings, ClassLoaderService classLoaderService) { + private SessionFactoryImplementor sessionFactory; + + public BeanValidationEventListener(ValidatorFactory factory, Map settings, ClassLoaderService classLoaderService) { traversableResolver = new HibernateTraversableResolver(); validator = factory.usingContext() @@ -63,9 +65,9 @@ public BeanValidationEventListener( @Override public void sessionFactoryCreated(SessionFactory factory) { - var implementor = factory.unwrap( SessionFactoryImplementor.class ); - implementor.getMappingMetamodel() - .forEachEntityDescriptor( entityPersister -> traversableResolver.addPersister( entityPersister, implementor ) ); + sessionFactory = factory.unwrap( SessionFactoryImplementor.class ); + sessionFactory.getMappingMetamodel() + .forEachEntityDescriptor( entityPersister -> traversableResolver.addPersister( entityPersister, sessionFactory ) ); } public boolean onPreInsert(PreInsertEvent event) { @@ -110,11 +112,21 @@ public void onPreUpdateCollection(PreCollectionUpdateEvent event) { final Object entity = castNonNull( event.getCollection().getOwner() ); validate( entity, - event.getSession().getEntityPersister( event.getAffectedOwnerEntityName(), entity ), + getEntityPersister( event.getSession(), event.getAffectedOwnerEntityName(), entity ), GroupsPerOperation.Operation.UPDATE ); } + private EntityPersister getEntityPersister(SharedSessionContractImplementor session, String entityName, Object entity) { + if ( session != null ) { + return session.getEntityPersister( entityName, entity ); + } + return entityName == null + ? sessionFactory.getMappingMetamodel().getEntityDescriptor( entity.getClass().getName() ) + : sessionFactory.getMappingMetamodel().getEntityDescriptor( entityName ) + .getSubclassEntityPersister( entity, sessionFactory ); + } + private void validate( T object, EntityPersister persister, diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/beanvalidation/CollectionActionsValidationStatelessTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/beanvalidation/CollectionActionsValidationStatelessTest.java new file mode 100644 index 000000000000..aff5004cd14d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/beanvalidation/CollectionActionsValidationStatelessTest.java @@ -0,0 +1,109 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.annotations.beanvalidation; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.constraints.Size; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@SessionFactory +@DomainModel(annotatedClasses = { + CollectionActionsValidationStatelessTest.Author.class, + CollectionActionsValidationStatelessTest.Book.class, +}) +@ServiceRegistry(settings = @Setting(name = AvailableSettings.JAKARTA_VALIDATION_MODE, value = "auto")) +@Jira("https://hibernate.atlassian.net/browse/HHH-19843") +public class CollectionActionsValidationStatelessTest { + + @Test + void smoke(SessionFactoryScope scope) { + scope.inStatelessTransaction( session -> { + final ConstraintViolationException e = assertThrows( ConstraintViolationException.class, () -> { + ArrayList books = new ArrayList<>(); + Author author = new Author( 1L, "first", "last", books ); + Book book = new Book( 10L, "", author ); + books.add( book ); + + session.upsertMultiple( List.of( author ) ); + } ); + assertThat( e.getConstraintViolations() ).hasSize( 1 ); + } ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncate(); + } + + @Table(name = "author") + @Entity + static class Author { + + public Author() { + } + + public Author(long id, String firstName, String lastName, List books) { + this.firstName = firstName; + this.lastName = lastName; + this.books = books; + this.id = id; + } + + @Id + Long id; + + String firstName; + + String lastName; + + @OneToMany + @JoinColumn(name = "bookId") + @Size(min = 10) + List books; + + } + + @Table(name = "book") + @Entity + static class Book { + + public Book() { + } + + public Book(long id, String title, Author author) { + this.id = id; + this.title = title; + this.author = author; + } + + @Id + Long id; + + String title; + + @ManyToOne + Author author; + } +} From e41bb02ca15e715cb336dc2fcf939047c2a00456 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Tue, 25 Nov 2025 15:29:45 +0100 Subject: [PATCH 005/181] Change how release_notes are discovered --- ci/release/Jenkinsfile | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile index 5278d3463e4d..05eb3569d4bc 100644 --- a/ci/release/Jenkinsfile +++ b/ci/release/Jenkinsfile @@ -222,15 +222,9 @@ pipeline { withEnv([ "DISABLE_REMOTE_GRADLE_CACHE=true" ]) { - def notesFiles = findFiles(glob: 'release_notes.md') - if ( notesFiles.length < 1 ) { - throw new IllegalStateException( "Could not locate `release_notes.md`" ) - } - if ( notesFiles.length > 1 ) { - throw new IllegalStateException( "Located more than 1 `release_notes.md`" ) - } + def ghReleaseNote = sh(script: 'realpath -e release_notes.md 2>/dev/null', returnStdout: true).trim() - sh ".release/scripts/publish.sh -j --notes=${notesFiles[0].path} ${env.SCRIPT_OPTIONS} ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION} ${env.GIT_BRANCH} " + sh ".release/scripts/publish.sh -j ${ghReleaseNote != '' ? '--notes=' + ghReleaseNote : ''} ${env.SCRIPT_OPTIONS} ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION} ${env.GIT_BRANCH} " } } } From e709838e9cd5bab5d81e7478f58d61e557da5dc6 Mon Sep 17 00:00:00 2001 From: Bastien JANSEN Date: Wed, 26 Nov 2025 17:50:27 +0100 Subject: [PATCH 006/181] HHH-19958 Implement the custom orm.xml `` tag --- .../xml/internal/XmlAnnotationHelper.java | 24 +++++++++ .../attr/BasicAttributeProcessing.java | 2 +- .../various/OrmXmlGeneratedTest.java | 52 +++++++++++++++++++ .../orm/test/annotations/various/Tractor.java | 13 +++++ .../test/annotations/generated/ormXml/orm.xml | 17 ++++++ 5 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/various/OrmXmlGeneratedTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/various/Tractor.java create mode 100644 hibernate-core/src/test/resources/org/hibernate/orm/test/annotations/generated/ormXml/orm.xml diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java index 36608dcaed68..54bb3cb56d32 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java @@ -20,6 +20,7 @@ import java.time.OffsetTime; import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; import java.util.UUID; import java.util.function.Consumer; @@ -32,6 +33,7 @@ import org.hibernate.annotations.ResultCheckStyle; import org.hibernate.annotations.SecondaryRow; import org.hibernate.boot.internal.LimitedCollectionClassification; +import org.hibernate.boot.jaxb.mapping.GenerationTiming; import org.hibernate.boot.jaxb.mapping.spi.JaxbAssociationOverrideImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbAttributeOverrideImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbBasicMapping; @@ -90,6 +92,7 @@ import org.hibernate.boot.models.xml.spi.XmlDocument; import org.hibernate.boot.models.xml.spi.XmlDocumentContext; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; +import org.hibernate.generator.EventType; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.models.ModelsException; @@ -479,6 +482,27 @@ public static void applyNationalized( memberDetails.applyAnnotationUsage( HibernateAnnotations.NATIONALIZED, xmlDocumentContext.getModelBuildingContext() ); } + public static void applyGenerated( + GenerationTiming timing, + MutableMemberDetails memberDetails, + XmlDocumentContext xmlDocumentContext) { + if ( timing == null ) { + return; + } + + EnumSet eventTypes = timing.getEventTypes(); + if ( eventTypes == null ) { + return; + } + + final GeneratedAnnotation generatedAnn = (GeneratedAnnotation) memberDetails.applyAnnotationUsage( + HibernateAnnotations.GENERATED, + xmlDocumentContext.getModelBuildingContext() + ); + + generatedAnn.event( eventTypes.toArray( new EventType[0] ) ); + } + public static void applyGeneratedValue( JaxbGeneratedValueImpl jaxbGeneratedValue, MutableMemberDetails memberDetails, diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/BasicAttributeProcessing.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/BasicAttributeProcessing.java index 60628ed0b286..8849ddd0df24 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/BasicAttributeProcessing.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/BasicAttributeProcessing.java @@ -78,8 +78,8 @@ else if ( jaxbBasic.getColumn() != null ) { XmlAnnotationHelper.applyLob( jaxbBasic.getLob(), memberDetails, xmlDocumentContext ); XmlAnnotationHelper.applyEnumerated( jaxbBasic.getEnumerated(), memberDetails, xmlDocumentContext ); XmlAnnotationHelper.applyNationalized( jaxbBasic.getNationalized(), memberDetails, xmlDocumentContext ); + XmlAnnotationHelper.applyGenerated( jaxbBasic.getGenerated(), memberDetails, xmlDocumentContext ); - // todo : value generation // todo : ... return memberDetails; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/various/OrmXmlGeneratedTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/various/OrmXmlGeneratedTest.java new file mode 100644 index 000000000000..e5f134418498 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/various/OrmXmlGeneratedTest.java @@ -0,0 +1,52 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.annotations.various; + +import org.hibernate.boot.Metadata; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.generator.EventType; +import org.hibernate.mapping.GeneratorCreator; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.testing.ServiceRegistryBuilder; +import org.hibernate.testing.orm.junit.BaseUnitTest; +import org.hibernate.testing.util.uuid.IdGeneratorCreationContext; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@BaseUnitTest +class OrmXmlGeneratedTest { + + @Test + void testOrmXmlDefinedGenerated() { + StandardServiceRegistry ssr = ServiceRegistryBuilder.buildServiceRegistry(); + + try { + MetadataSources ms = new MetadataSources( ssr ); + ms.addResource( "org/hibernate/orm/test/annotations/generated/ormXml/orm.xml" ); + + Metadata metadata = ms.buildMetadata(); + + PersistentClass entityBinding = metadata.getEntityBinding( Tractor.class.getName() ); + GeneratorCreator generator = entityBinding + .getProperty( "serialNumber" ) + .getValueGeneratorCreator(); + + assertThat( generator ) + .extracting( creator -> creator.createGenerator( + new IdGeneratorCreationContext( (MetadataImplementor) metadata, entityBinding.getRootClass() ) + ) ) + .satisfies( gen -> + assertThat( gen.getEventTypes() ) + .containsExactly( EventType.INSERT, EventType.UPDATE, EventType.FORCE_INCREMENT ) + ); + } + finally { + ServiceRegistryBuilder.destroy( ssr ); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/various/Tractor.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/various/Tractor.java new file mode 100644 index 000000000000..4c2b50d71251 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/various/Tractor.java @@ -0,0 +1,13 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.annotations.various; + +public class Tractor { + + public Long id; + public String model; + public int serialNumber; + +} diff --git a/hibernate-core/src/test/resources/org/hibernate/orm/test/annotations/generated/ormXml/orm.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/annotations/generated/ormXml/orm.xml new file mode 100644 index 000000000000..6b91fa2599d9 --- /dev/null +++ b/hibernate-core/src/test/resources/org/hibernate/orm/test/annotations/generated/ormXml/orm.xml @@ -0,0 +1,17 @@ + + + + + + + + + ALWAYS + + + + From 72dae39eb4590ed1d012dac9ee6c992328742a0d Mon Sep 17 00:00:00 2001 From: Rob Green Date: Sun, 12 Oct 2025 19:03:23 -0400 Subject: [PATCH 007/181] HHH-18871 allow collections to be mapped correctly while determining navigation path --- .../internal/ResultSetMappingProcessor.java | 10 +- .../test/query/NativeQueryNestedTreeTest.java | 108 ++++++++++++++++++ 2 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/query/NativeQueryNestedTreeTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java index 0ed5efced51c..0780ce1cd8d3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java @@ -16,6 +16,7 @@ import org.hibernate.LockMode; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.loader.internal.AliasConstantsHelper; +import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; @@ -260,16 +261,19 @@ private List columnNames( private NavigablePath determineNavigablePath(LegacyFetchBuilder fetchBuilder) { final var ownerResult = alias2Return.get( fetchBuilder.getOwnerAlias() ); - final NavigablePath path; + final NavigablePath basePath; if ( ownerResult instanceof NativeQuery.RootReturn rootReturn ) { - path = rootReturn.getNavigablePath(); + basePath = rootReturn.getNavigablePath(); } else if ( ownerResult instanceof DynamicFetchBuilderLegacy dynamicFetchBuilderLegacy ) { - path = determineNavigablePath( dynamicFetchBuilderLegacy ); + basePath = determineNavigablePath( dynamicFetchBuilderLegacy ); } else { throw new AssertionFailure( "Unexpected fetch builder" ); } + final NavigablePath path = alias2CollectionPersister.containsKey( fetchBuilder.getOwnerAlias() ) + ? basePath.append( CollectionPart.Nature.ELEMENT.getName() ) + : basePath; return path.append( fetchBuilder.getFetchable().getFetchableName() ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/NativeQueryNestedTreeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/NativeQueryNestedTreeTest.java new file mode 100644 index 000000000000..e5e2396b5ad1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/NativeQueryNestedTreeTest.java @@ -0,0 +1,108 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.query; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +/** + * This reproduces an issue in Hibernate 6 parsing native queries. + */ +@DomainModel( + annotatedClasses = { + NativeQueryNestedTreeTest.Tree.class, + NativeQueryNestedTreeTest.Forest.class + } +) +@SessionFactory +@Jira("https://hibernate.atlassian.net/browse/HHH-18871") +public class NativeQueryNestedTreeTest { + + @Test + public void test(SessionFactoryScope scope) { + // We want to make sure 'Could not locate TableGroup' no longer is thrown + assertDoesNotThrow( () -> scope.inTransaction( session -> + session.createNativeQuery( """ + select {t.*}, {t2.*}, {t3.*} + from tree t + inner join tree t2 on t2.parent_id = t.id + inner join tree t3 on t3.parent_id = t2.id + """, Tree.class ) + .addEntity( "t", Tree.class ) + .addJoin( "t2", "t.children" ) + .addJoin( "t3", "t2.children" ) + .list() + ) ); + + assertDoesNotThrow( () -> scope.inTransaction( session -> + session.createNativeQuery( """ + select {t.*}, {t2.*}, {t3.*}, {t4.*} + from tree t + inner join tree t2 on t2.parent_id = t.id + inner join tree t3 on t3.parent_id = t2.id + inner join tree t4 on t4.parent_id = t3.id + """, Tree.class ) + .addEntity( "t", Tree.class ) + .addJoin( "t2", "t.children" ) + .addJoin( "t3", "t2.children" ) + .addJoin( "t4", "t3.children" ) + .list() + ) ); + + assertDoesNotThrow( () -> scope.inTransaction( session -> + session.createNativeQuery( """ + select {f.*}, {t.*}, {t2.*} + from forest f + inner join tree t on t.parent_id is null + inner join tree t2 on t2.parent_id = t.id + """, Forest.class ) + .addEntity( "f", Forest.class ) + .addJoin( "t", "f.trees" ) + .addJoin( "t2", "t.children" ) + .list() + ) ); + } + + @Entity(name = "Tree") + @Table(name = "tree") + public static class Tree { + @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JoinColumn(name = "parent_id") + private Tree parent; + @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + private Set children = new HashSet<>(); + @Id + @GeneratedValue + private long id; + } + + @Entity(name = "Forest") + @Table(name = "forest") + public static class Forest { + @Id + @GeneratedValue + private Long id; + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JoinColumn(name = "forest_id") + private Set trees = new HashSet<>(); + } +} From 2269a0065108f0476e0977795a497ddcd8f88a4c Mon Sep 17 00:00:00 2001 From: wafflejuice Date: Sat, 1 Mar 2025 16:33:26 +0900 Subject: [PATCH 008/181] HHH-19215 handle queries with straight_join or just order by --- .../java/org/hibernate/dialect/Dialect.java | 12 +++------- .../org/hibernate/dialect/DialectTest.java | 23 ++++++++++++++----- .../test/jpa/query/NamedQueryCommentTest.java | 2 +- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 7601ab4ce695..e5b3f1b05564 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -338,7 +338,7 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun private static final Pattern ESCAPE_CLOSING_COMMENT_PATTERN = Pattern.compile( "\\*/" ); private static final Pattern ESCAPE_OPENING_COMMENT_PATTERN = Pattern.compile( "/\\*" ); private static final Pattern QUERY_PATTERN = Pattern.compile( - "^\\s*(select\\b.+?\\bfrom\\b.+?)(\\b(?:natural )?(?:left |right |full )?(?:inner |outer |cross )?join.+?\\b)?(\\bwhere\\b.+?)$", + "^\\s*(select\\s.+?\\sfrom\\s.+?)(\\s(?:(?:natural)?\\s*(?:left|right|full)?\\s*(?:inner|outer|cross)?\\s*join|straight_join)\\s.+?)?(\\swhere\\s.+?)?(\\sorder\\s+by\\s.+?)?$", Pattern.CASE_INSENSITIVE); //needed for converting precision from decimal to binary digits @@ -5242,14 +5242,8 @@ public String addSqlHintOrComment(String sql, QueryOptions queryOptions, boolean public static String addUseIndexQueryHint(String query, String hints) { final Matcher matcher = QUERY_PATTERN.matcher( query ); if ( matcher.matches() && matcher.groupCount() > 1 ) { - final String startToken = matcher.group(1); - // Null if there is no join in the query - final String joinToken = matcher.group(2); - final String endToken = matcher.group(3); - return startToken - + " use index (" + hints + ") " - + ( joinToken == null ? "" : joinToken ) - + endToken; + final String startToken = matcher.group( 1 ); + return startToken + " use index (" + hints + ")" + query.substring( startToken.length() ); } else { return query; diff --git a/hibernate-core/src/test/java/org/hibernate/dialect/DialectTest.java b/hibernate-core/src/test/java/org/hibernate/dialect/DialectTest.java index e2493286e83b..d45ec967a3c7 100644 --- a/hibernate-core/src/test/java/org/hibernate/dialect/DialectTest.java +++ b/hibernate-core/src/test/java/org/hibernate/dialect/DialectTest.java @@ -24,21 +24,32 @@ static Stream _addQueryHints() { final String hints = "MY_INDEX"; final String simpleQuery = "select COUNT(*) from TEST t1_0 where column1 = 'value'"; - builder.add( - Arguments.of("Simple query : hint", - "select COUNT(*) from TEST t1_0 use index (MY_INDEX) where column1 = 'value'", simpleQuery, hints)); + builder.add(Arguments.of("Simple query : hint", + "select COUNT(*) from TEST t1_0 use index (MY_INDEX) where column1 = 'value'", simpleQuery, hints)); + final String limitQuery = "select COUNT(*) from TEST t1_0 order by column1 limit 1"; + builder.add(Arguments.of("Limit query : hint", + "select COUNT(*) from TEST t1_0 use index (MY_INDEX) order by column1 limit 1", + limitQuery, hints)); final String joinQueryUsing = "select COUNT(*) from TEST t1_0 join TEST2 t2_0 using(column2) where field = 'value'"; builder.add(Arguments.of("Join query with using : hint", - "select COUNT(*) from TEST t1_0 use index (MY_INDEX) join TEST2 t2_0 using(column2) where field = 'value'", + "select COUNT(*) from TEST t1_0 use index (MY_INDEX) join TEST2 t2_0 using(column2) where field = 'value'", joinQueryUsing, hints)); final String joinQueryOn = "select COUNT(*) from TEST t1_0 join TEST2 t2_0 on t1_0.column2 = t2_0.column2 where field = 'value'"; builder.add(Arguments.of("Join query with on : hint", - "select COUNT(*) from TEST t1_0 use index (MY_INDEX) join TEST2 t2_0 on t1_0.column2 = t2_0.column2 where field = 'value'", + "select COUNT(*) from TEST t1_0 use index (MY_INDEX) join TEST2 t2_0 on t1_0.column2 = t2_0.column2 where field = 'value'", joinQueryOn, hints)); final String leftJoinQuery = "select COUNT(*) from TEST t1_0 left join TEST2 t2_0 on t1_0.column2 = t2_0.column2 and t1_0.column3 = t2_0.column3 where field = 'value'"; builder.add(Arguments.of("Left join query with on : hint", - "select COUNT(*) from TEST t1_0 use index (MY_INDEX) left join TEST2 t2_0 on t1_0.column2 = t2_0.column2 and t1_0.column3 = t2_0.column3 where field = 'value'", + "select COUNT(*) from TEST t1_0 use index (MY_INDEX) left join TEST2 t2_0 on t1_0.column2 = t2_0.column2 and t1_0.column3 = t2_0.column3 where field = 'value'", leftJoinQuery, hints)); + final String straightJoinQueryUsing = "select COUNT(*) from TEST t1_0 straight_join TEST2 t2_0 using(column2) where field = 'value'"; + builder.add(Arguments.of("Straight join query with using : hint", + "select COUNT(*) from TEST t1_0 use index (MY_INDEX) straight_join TEST2 t2_0 using(column2) where field = 'value'", + straightJoinQueryUsing, hints)); + final String straightJoinQueryOn = "select COUNT(*) from TEST t1_0 straight_join TEST2 t2_0 on t1_0.column2 = t2_0.column2 where field = 'value'"; + builder.add(Arguments.of("Straight join query with on : hint", + "select COUNT(*) from TEST t1_0 use index (MY_INDEX) straight_join TEST2 t2_0 on t1_0.column2 = t2_0.column2 where field = 'value'", + straightJoinQueryOn, hints)); return builder.build(); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryCommentTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryCommentTest.java index 5066d43ec9fd..e5e882f45bd2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryCommentTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/NamedQueryCommentTest.java @@ -219,7 +219,7 @@ public void testSelectNamedNativeQueryWithQueryHintUsingIndex(EntityManagerFacto statementInspector.assertExecutedCount(1); statementInspector.assertExecuted( - "/* COMMENT_SELECT_INDEX_game_title */ select g1_0.id,g1_0.title from game g1_0 use index (idx_game_id) where g1_0.title=?" + "/* COMMENT_SELECT_INDEX_game_title */ select g1_0.id,g1_0.title from game g1_0 use index (idx_game_id) where g1_0.title=?" ); } ); From c1d47608cb3436e0d9e097f6c26021f2fc0109fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 20 Aug 2025 10:41:09 +0200 Subject: [PATCH 009/181] HHH-19725 Test upsert for entity type that only declares an ID attribute --- .../orm/test/stateless/UpsertTest.java | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/UpsertTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/UpsertTest.java index c18aa232c08e..fd739add12d2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/UpsertTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/UpsertTest.java @@ -18,9 +18,10 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; @SessionFactory(useCollectingStatementInspector = true) -@DomainModel(annotatedClasses = UpsertTest.Record.class) +@DomainModel(annotatedClasses = {UpsertTest.Record.class, UpsertTest.IdOnly.class}) public class UpsertTest { @Test void test(SessionFactoryScope scope) { scope.getSessionFactory().getSchemaManager().truncate(); @@ -96,6 +97,19 @@ public class UpsertTest { scope.inStatelessTransaction(s-> assertDoesNotThrow(() -> s.upsert(new Record(123L,null, null))) ); } + @Test void testIdOnly(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncate(); + + scope.inStatelessTransaction(s-> { + s.upsert(new IdOnly(123L)); + s.upsert(new IdOnly(456L)); + }); + scope.inStatelessTransaction(s-> { + assertNotNull(s.get( IdOnly.class,123L)); + assertNotNull(s.get( IdOnly.class,456L)); + }); + } + @Entity(name = "Record") static class Record { @Id Long id; @@ -116,4 +130,16 @@ static class Record { Record() { } } + + @Entity(name = "IdOnly") + static class IdOnly { + @Id Long id; + + IdOnly(Long id) { + this.id = id; + } + + IdOnly() { + } + } } From eacb8361af412dbcc3ea92eb9dd21ed6e29cfbdc Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Thu, 28 Aug 2025 17:14:34 +0200 Subject: [PATCH 010/181] HHH-18217 Properly handle empty value bindings for merge operations --- .../dialect/MySQLDeleteOrUpsertOperation.java | 1 + .../sql/ast/MariaDBSqlAstTranslator.java | 2 +- .../sql/ast/MySQLSqlAstTranslator.java | 2 +- ...AstTranslatorWithOnDuplicateKeyUpdate.java | 74 ++++++++++++------- .../java/org/hibernate/jdbc/Expectation.java | 25 +++++++ .../entity/AbstractEntityPersister.java | 9 +-- .../entity/mutation/MergeCoordinator.java | 46 ++++++++++++ .../persister/entity/mutation/TableSet.java | 7 ++ .../mutation/UpdateCoordinatorStandard.java | 7 +- .../ast/spi/SqlAstTranslatorWithMerge.java | 25 ++++--- .../ast/spi/SqlAstTranslatorWithUpsert.java | 25 ++++--- .../model/ast/builder/TableMergeBuilder.java | 4 - .../model/jdbc/DeleteOrUpsertOperation.java | 4 +- .../sql/model/jdbc/MergeOperation.java | 11 ++- .../jdbc/OptionalTableUpdateOperation.java | 12 ++- .../sql/model/jdbc/UpsertOperation.java | 11 ++- .../orm/test/stateless/UpsertTest.java | 57 +++++++++++++- 17 files changed, 252 insertions(+), 70 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDeleteOrUpsertOperation.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDeleteOrUpsertOperation.java index 53d4a5cf9f87..06775f55c0f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDeleteOrUpsertOperation.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDeleteOrUpsertOperation.java @@ -22,6 +22,7 @@ /** * @author Jan Schatteman */ +@Deprecated(forRemoval = true) public class MySQLDeleteOrUpsertOperation extends DeleteOrUpsertOperation { private Expectation customExpectation; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/MariaDBSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/MariaDBSqlAstTranslator.java index 30958f0fe4eb..ffb43eba0725 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/MariaDBSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/MariaDBSqlAstTranslator.java @@ -416,7 +416,7 @@ INSERT INTO employees (id, name, salary, version) salary = values(salary) */ @Override - protected void renderUpdatevalue(ColumnValueBinding columnValueBinding) { + protected void renderUpdateValue(ColumnValueBinding columnValueBinding) { appendSql( "values(" ); appendSql( columnValueBinding.getColumnReference().getColumnExpression() ); appendSql( ")" ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/MySQLSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/MySQLSqlAstTranslator.java index f71014d744f8..1a69c8c351a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/MySQLSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/MySQLSqlAstTranslator.java @@ -449,7 +449,7 @@ protected void renderNewRowAlias() { } @Override - protected void renderUpdatevalue(ColumnValueBinding columnValueBinding) { + protected void renderUpdateValue(ColumnValueBinding columnValueBinding) { renderAlias(); appendSql( "." ); appendSql( columnValueBinding.getColumnReference().getColumnExpression() ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/SqlAstTranslatorWithOnDuplicateKeyUpdate.java b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/SqlAstTranslatorWithOnDuplicateKeyUpdate.java index 73fab31d26ca..2ad1b55b760f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/SqlAstTranslatorWithOnDuplicateKeyUpdate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/SqlAstTranslatorWithOnDuplicateKeyUpdate.java @@ -5,8 +5,9 @@ package org.hibernate.dialect.sql.ast; -import org.hibernate.dialect.MySQLDeleteOrUpsertOperation; +import org.hibernate.StaleStateException; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.jdbc.Expectation; import org.hibernate.persister.entity.mutation.EntityTableMapping; import org.hibernate.sql.ast.spi.SqlAstTranslatorWithUpsert; import org.hibernate.sql.ast.tree.Statement; @@ -14,8 +15,10 @@ import org.hibernate.sql.model.MutationOperation; import org.hibernate.sql.model.ast.ColumnValueBinding; import org.hibernate.sql.model.internal.OptionalTableUpdate; +import org.hibernate.sql.model.jdbc.DeleteOrUpsertOperation; import org.hibernate.sql.model.jdbc.UpsertOperation; +import java.sql.PreparedStatement; import java.util.List; /** @@ -37,10 +40,11 @@ public MutationOperation createMergeOperation(OptionalTableUpdate optionalTableU optionalTableUpdate.getMutatingTable().getTableMapping(), optionalTableUpdate.getMutationTarget(), getSql(), + new MySQLRowCountExpectation(), getParameterBinders() ); - return new MySQLDeleteOrUpsertOperation( + return new DeleteOrUpsertOperation( optionalTableUpdate.getMutationTarget(), (EntityTableMapping) optionalTableUpdate.getMutatingTable().getTableMapping(), upsertOperation, @@ -48,6 +52,19 @@ public MutationOperation createMergeOperation(OptionalTableUpdate optionalTableU ); } + private static class MySQLRowCountExpectation implements Expectation { + @Override + public final void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String sql) { + if ( rowCount > 2 ) { + throw new StaleStateException( + "Unexpected row count" + + " (the expected row count for an ON DUPLICATE KEY UPDATE statement should be either 0, 1 or 2 )" + + " [" + sql + "]" + ); + } + } + } + @Override protected void renderUpsertStatement(OptionalTableUpdate optionalTableUpdate) { renderInsertInto( optionalTableUpdate ); @@ -56,34 +73,39 @@ protected void renderUpsertStatement(OptionalTableUpdate optionalTableUpdate) { } protected void renderInsertInto(OptionalTableUpdate optionalTableUpdate) { - appendSql( "insert into " ); + if ( optionalTableUpdate.getValueBindings().isEmpty() ) { + appendSql( "insert ignore into " ); + } + else { + appendSql( "insert into " ); + } appendSql( optionalTableUpdate.getMutatingTable().getTableName() ); - appendSql( " (" ); + appendSql( " " ); final List keyBindings = optionalTableUpdate.getKeyBindings(); + char separator = '('; for ( ColumnValueBinding keyBinding : keyBindings ) { + appendSql( separator ); appendSql( keyBinding.getColumnReference().getColumnExpression() ); - appendSql( ',' ); + separator = ','; } optionalTableUpdate.forEachValueBinding( (columnPosition, columnValueBinding) -> { - appendSql( columnValueBinding.getColumnReference().getColumnExpression() ); - if ( columnPosition != optionalTableUpdate.getValueBindings().size() - 1 ) { - appendSql( ',' ); - } + appendSql( ',' ); + appendSql( columnValueBinding.getColumnReference().getColumnExpression() ); } ); - appendSql( ") values (" ); + appendSql( ") values " ); + separator = '('; for ( ColumnValueBinding keyBinding : keyBindings ) { + appendSql( separator ); keyBinding.getValueExpression().accept( this ); - appendSql( ',' ); + separator = ','; } optionalTableUpdate.forEachValueBinding( (columnPosition, columnValueBinding) -> { - if ( columnPosition > 0 ) { - appendSql( ',' ); - } + appendSql( ',' ); columnValueBinding.getValueExpression().accept( this ); } ); appendSql(") "); @@ -94,19 +116,21 @@ protected void renderNewRowAlias() { } protected void renderOnDuplicateKeyUpdate(OptionalTableUpdate optionalTableUpdate) { - appendSql( "on duplicate key update " ); - optionalTableUpdate.forEachValueBinding( (columnPosition, columnValueBinding) -> { - final String columnName = columnValueBinding.getColumnReference().getColumnExpression(); - if ( columnPosition > 0 ) { - appendSql( ',' ); - } - appendSql( columnName ); - append( " = " ); - renderUpdatevalue( columnValueBinding ); - } ); + if ( !optionalTableUpdate.getValueBindings().isEmpty() ) { + appendSql( "on duplicate key update " ); + optionalTableUpdate.forEachValueBinding( (columnPosition, columnValueBinding) -> { + final String columnName = columnValueBinding.getColumnReference().getColumnExpression(); + if ( columnPosition > 0 ) { + appendSql( ',' ); + } + appendSql( columnName ); + append( " = " ); + renderUpdateValue( columnValueBinding ); + } ); + } } - protected void renderUpdatevalue(ColumnValueBinding columnValueBinding) { + protected void renderUpdateValue(ColumnValueBinding columnValueBinding) { } } diff --git a/hibernate-core/src/main/java/org/hibernate/jdbc/Expectation.java b/hibernate-core/src/main/java/org/hibernate/jdbc/Expectation.java index 941eb5e65d97..3e29db80f41e 100644 --- a/hibernate-core/src/main/java/org/hibernate/jdbc/Expectation.java +++ b/hibernate-core/src/main/java/org/hibernate/jdbc/Expectation.java @@ -179,6 +179,31 @@ protected int expectedRowCount() { } } + /** + * Row count checking. A row count is an integer value returned by + * {@link java.sql.PreparedStatement#executeUpdate()} or + * {@link java.sql.Statement#executeBatch()}. The row count is checked + * against an expected value, but is also allowed to be 0. + * For example, the expected row count for an {@code UPSERT} statement is 0 or 1. + */ + class OptionalRowCount implements Expectation { + @Override + public final void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String sql) { + if ( rowCount != 0 ) { + if ( batchPosition < 0 ) { + checkNonBatched( expectedRowCount(), rowCount, sql ); + } + else { + checkBatched( expectedRowCount(), rowCount, batchPosition, sql ); + } + } + } + + protected int expectedRowCount() { + return 1; + } + } + /** * Essentially identical to {@link RowCount} except that the row count * is obtained via an output parameter of a {@linkplain CallableStatement diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 76d9652c8e94..d64878d30bde 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -3461,14 +3461,7 @@ protected UpdateCoordinator buildUpdateCoordinator() { } protected UpdateCoordinator buildMergeCoordinator() { - // we only have updates to issue for entities with one or more singular attributes - for ( int i = 0; i < attributeMappings.size(); i++ ) { - if ( attributeMappings.get( i ) instanceof SingularAttributeMapping ) { - return new MergeCoordinator( this, factory ); - } - } - // otherwise, nothing to update - return new UpdateCoordinatorNoOp( this ); + return new MergeCoordinator( this, factory ); } protected DeleteCoordinator buildDeleteCoordinator() { diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/MergeCoordinator.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/MergeCoordinator.java index 1b6f78497dc9..b8733df5a32d 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/MergeCoordinator.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/MergeCoordinator.java @@ -5,6 +5,7 @@ package org.hibernate.persister.entity.mutation; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.sql.model.MutationOperation; import org.hibernate.sql.model.ast.builder.AbstractTableUpdateBuilder; @@ -26,4 +27,49 @@ protected AbstractTableUpdateBuilder newTableUp return new TableMergeBuilder<>( entityPersister(), tableMapping, factory() ); } + @Override + protected UpdateValuesAnalysisImpl analyzeUpdateValues( + Object entity, + Object[] values, + Object oldVersion, + Object[] oldValues, + int[] dirtyAttributeIndexes, + InclusionChecker inclusionChecker, + InclusionChecker lockingChecker, + InclusionChecker dirtinessChecker, + Object rowId, + boolean forceDynamicUpdate, + SharedSessionContractImplementor session) { + final UpdateValuesAnalysisImpl updateValuesAnalysis = super.analyzeUpdateValues( + entity, + values, + oldVersion, + oldValues, + dirtyAttributeIndexes, + inclusionChecker, + lockingChecker, + dirtinessChecker, + rowId, + forceDynamicUpdate, + session + ); + if ( oldValues == null ) { + final TableSet tablesNeedingUpdate = updateValuesAnalysis.getTablesNeedingUpdate(); + final TableSet tablesWithNonNullValues = updateValuesAnalysis.getTablesWithNonNullValues(); + final TableSet tablesWithPreviousNonNullValues = updateValuesAnalysis.getTablesWithPreviousNonNullValues(); + for ( EntityTableMapping tableMapping : entityPersister().getTableMappings() ) { + // Need to upsert into all non-optional table mappings + if ( !tableMapping.isOptional() ) { + // If the table was previously not needing an update, remove it from tablesWithPreviousNonNullValues + // to avoid triggering a delete-statement for this operation + if ( !tablesNeedingUpdate.contains( tableMapping ) ) { + tablesWithPreviousNonNullValues.remove( tableMapping ); + } + tablesNeedingUpdate.add( tableMapping ); + tablesWithNonNullValues.add( tableMapping ); + } + } + } + return updateValuesAnalysis; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/TableSet.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/TableSet.java index 4d2de0f42b09..e8fb90cf818c 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/TableSet.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/TableSet.java @@ -34,6 +34,13 @@ public void add(final TableMapping tableMapping) { bits.set( tableMapping.getRelativePosition() ); } + public void remove(final TableMapping tableMapping) { + if ( bits != null ) { + assert addForChecks( tableMapping ); + bits.set( tableMapping.getRelativePosition(), false ); + } + } + public boolean isEmpty() { return bits == null; } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java index f2c9fc3f2117..c6e15517c607 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java @@ -600,7 +600,7 @@ protected boolean[] getPropertiesToUpdate(final int[] dirtyProperties, final boo } } - private UpdateValuesAnalysisImpl analyzeUpdateValues( + protected UpdateValuesAnalysisImpl analyzeUpdateValues( Object entity, Object[] values, Object oldVersion, @@ -1018,7 +1018,10 @@ protected BatchKey getVersionUpdateBatchkey(){ } private boolean resultCheck( - Object id, PreparedStatementDetails statementDetails, int affectedRowCount, int batchPosition) { + Object id, + PreparedStatementDetails statementDetails, + int affectedRowCount, + int batchPosition) { return identifiedResultsCheck( statementDetails, affectedRowCount, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstTranslatorWithMerge.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstTranslatorWithMerge.java index ee1cee398c5c..2368ac99e63b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstTranslatorWithMerge.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstTranslatorWithMerge.java @@ -8,6 +8,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.StringHelper; +import org.hibernate.jdbc.Expectation; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.model.ast.ColumnValueBinding; @@ -44,6 +45,10 @@ public MergeOperation createMergeOperation(OptionalTableUpdate optionalTableUpda optionalTableUpdate.getMutatingTable().getTableMapping(), optionalTableUpdate.getMutationTarget(), getSql(), + // Without value bindings, the upsert may have an update count of 0 + optionalTableUpdate.getValueBindings().isEmpty() + ? new Expectation.OptionalRowCount() + : new Expectation.RowCount(), getParameterBinders() ); } @@ -232,16 +237,18 @@ protected void renderMergeUpdate(OptionalTableUpdate optionalTableUpdate) { final List valueBindings = optionalTableUpdate.getValueBindings(); final List optimisticLockBindings = optionalTableUpdate.getOptimisticLockBindings(); - renderWhenMatched( optimisticLockBindings ); - appendSql( " then update set " ); - for ( int i = 0; i < valueBindings.size(); i++ ) { - final ColumnValueBinding binding = valueBindings.get( i ); - if ( i > 0 ) { - appendSql( ", " ); + if ( !valueBindings.isEmpty() ) { + renderWhenMatched( optimisticLockBindings ); + appendSql( " then update set " ); + for ( int i = 0; i < valueBindings.size(); i++ ) { + final ColumnValueBinding binding = valueBindings.get( i ); + if ( i > 0 ) { + appendSql( ", " ); + } + binding.getColumnReference().appendColumnForWrite( this, null ); + appendSql( "=" ); + binding.getColumnReference().appendColumnForWrite( this, "s" ); } - binding.getColumnReference().appendColumnForWrite( this, null ); - appendSql( "=" ); - binding.getColumnReference().appendColumnForWrite( this, "s" ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstTranslatorWithUpsert.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstTranslatorWithUpsert.java index b914a4270dab..e594d9a5f513 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstTranslatorWithUpsert.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstTranslatorWithUpsert.java @@ -7,6 +7,7 @@ import java.util.List; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.jdbc.Expectation; import org.hibernate.persister.entity.mutation.EntityTableMapping; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; @@ -37,6 +38,10 @@ public MutationOperation createMergeOperation(OptionalTableUpdate optionalTableU optionalTableUpdate.getMutatingTable().getTableMapping(), optionalTableUpdate.getMutationTarget(), getSql(), + // Without value bindings, the upsert may have an update count of 0 + optionalTableUpdate.getValueBindings().isEmpty() + ? new Expectation.OptionalRowCount() + : new Expectation.RowCount(), getParameterBinders() ); @@ -203,17 +208,19 @@ protected void renderMergeUpdate(OptionalTableUpdate optionalTableUpdate) { final List valueBindings = optionalTableUpdate.getValueBindings(); final List optimisticLockBindings = optionalTableUpdate.getOptimisticLockBindings(); - appendSql( "when matched then update set " ); - for ( int i = 0; i < valueBindings.size(); i++ ) { - final ColumnValueBinding binding = valueBindings.get( i ); - if ( i > 0 ) { - appendSql( ", " ); + if ( !valueBindings.isEmpty() ) { + appendSql( "when matched then update set " ); + for ( int i = 0; i < valueBindings.size(); i++ ) { + final ColumnValueBinding binding = valueBindings.get( i ); + if ( i > 0 ) { + appendSql( ", " ); + } + binding.getColumnReference().appendColumnForWrite( this, "t" ); + appendSql( "=" ); + binding.getColumnReference().appendColumnForWrite( this, "s" ); } - binding.getColumnReference().appendColumnForWrite( this, "t" ); - appendSql( "=" ); - binding.getColumnReference().appendColumnForWrite( this, "s" ); + renderMatchedWhere( optimisticLockBindings ); } - renderMatchedWhere( optimisticLockBindings ); } private void renderMatchedWhere(List optimisticLockBindings) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableMergeBuilder.java b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableMergeBuilder.java index 75054ef7e78d..754676cf0f34 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableMergeBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/TableMergeBuilder.java @@ -12,7 +12,6 @@ import org.hibernate.sql.model.ast.MutatingTableReference; import org.hibernate.sql.model.ast.RestrictedTableMutation; import org.hibernate.sql.model.internal.OptionalTableUpdate; -import org.hibernate.sql.model.internal.TableUpdateNoSet; import java.util.List; @@ -39,9 +38,6 @@ public TableMergeBuilder( @Override public RestrictedTableMutation buildMutation() { final List valueBindings = combine( getValueBindings(), getKeyBindings(), getLobValueBindings() ); - if ( valueBindings.isEmpty() ) { - return (RestrictedTableMutation) new TableUpdateNoSet( getMutatingTable(), getMutationTarget() ); - } // TODO: add getMergeDetails() // if ( getMutatingTable().getTableMapping().getUpdateDetails().getCustomSql() != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/DeleteOrUpsertOperation.java b/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/DeleteOrUpsertOperation.java index 5042cc87be35..2a759c77b928 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/DeleteOrUpsertOperation.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/DeleteOrUpsertOperation.java @@ -41,8 +41,6 @@ public class DeleteOrUpsertOperation implements SelfExecutingUpdateOperation { private final OptionalTableUpdate optionalTableUpdate; - private final Expectation expectation = getExpectation(); - public DeleteOrUpsertOperation( EntityMutationTarget mutationTarget, EntityTableMapping tableMapping, @@ -233,6 +231,6 @@ public OptionalTableUpdate getOptionalTableUpdate() { } protected Expectation getExpectation() { - return new Expectation.RowCount(); + return upsertOperation.getExpectation(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/MergeOperation.java b/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/MergeOperation.java index 0e1bf7d0dd94..ad7dabcd7ccd 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/MergeOperation.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/MergeOperation.java @@ -23,7 +23,16 @@ public MergeOperation( MutationTarget mutationTarget, String sql, List parameterBinders) { - super( tableDetails, mutationTarget, sql, false, new Expectation.RowCount(), parameterBinders ); + this( tableDetails, mutationTarget, sql, new Expectation.RowCount(), parameterBinders ); + } + + public MergeOperation( + TableMapping tableDetails, + MutationTarget mutationTarget, + String sql, + Expectation expectation, + List parameterBinders) { + super( tableDetails, mutationTarget, sql, false, expectation, parameterBinders ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateOperation.java b/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateOperation.java index 70362dc28fb4..7978e09a8080 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateOperation.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateOperation.java @@ -153,11 +153,17 @@ public void performMutation( performInsert( jdbcValueBindings, session ); } catch (ConstraintViolationException cve) { - throw cve.getKind() == UNIQUE + if ( cve.getKind() == UNIQUE ) { + // Ignore primary key violation if the insert is composed of just the primary key + if ( !valueBindings.isEmpty() ) { // assume it was the primary key constraint which was violated, // due to a new version of the row existing in the database - ? new StaleStateException( mutationTarget.getRolePath(), cve ) - : cve; + throw new StaleStateException( mutationTarget.getRolePath(), cve ); + } + } + else { + throw cve; + } } } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/UpsertOperation.java b/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/UpsertOperation.java index 6ba371d381c8..23b671724d4c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/UpsertOperation.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/UpsertOperation.java @@ -23,7 +23,16 @@ public UpsertOperation( MutationTarget mutationTarget, String sql, List parameterBinders) { - super( tableDetails, mutationTarget, sql, false, new Expectation.RowCount(), parameterBinders ); + this( tableDetails, mutationTarget, sql, new Expectation.RowCount(), parameterBinders ); + } + + public UpsertOperation( + TableMapping tableDetails, + MutationTarget mutationTarget, + String sql, + Expectation expectation, + List parameterBinders) { + super( tableDetails, mutationTarget, sql, false, expectation, parameterBinders ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/UpsertTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/UpsertTest.java index fd739add12d2..44fd0b57bff1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/UpsertTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/UpsertTest.java @@ -6,6 +6,8 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; import org.hibernate.testing.jdbc.SQLStatementInspector; @@ -21,7 +23,12 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; @SessionFactory(useCollectingStatementInspector = true) -@DomainModel(annotatedClasses = {UpsertTest.Record.class, UpsertTest.IdOnly.class}) +@DomainModel(annotatedClasses = { + UpsertTest.Record.class, + UpsertTest.IdOnly.class, + UpsertTest.IdOnlyIntermediate.class, + UpsertTest.IdOnlySubtype.class +}) public class UpsertTest { @Test void test(SessionFactoryScope scope) { scope.getSessionFactory().getSchemaManager().truncate(); @@ -102,11 +109,32 @@ public class UpsertTest { scope.inStatelessTransaction(s-> { s.upsert(new IdOnly(123L)); - s.upsert(new IdOnly(456L)); }); scope.inStatelessTransaction(s-> { assertNotNull(s.get( IdOnly.class,123L)); - assertNotNull(s.get( IdOnly.class,456L)); + }); + scope.inStatelessTransaction(s-> { + s.upsert(new IdOnly(123L)); + }); + scope.inStatelessTransaction(s-> { + assertNotNull(s.get( IdOnly.class,123L)); + }); + } + + @Test void testIdOnlySubtype(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncate(); + + scope.inStatelessTransaction(s-> { + s.upsert(new IdOnlySubtype(123L)); + }); + scope.inStatelessTransaction(s-> { + assertNotNull(s.get( IdOnlySubtype.class,123L)); + }); + scope.inStatelessTransaction(s-> { + s.upsert(new IdOnlySubtype(123L)); + }); + scope.inStatelessTransaction(s-> { + assertNotNull(s.get( IdOnlySubtype.class,123L)); }); } @@ -132,6 +160,7 @@ static class Record { } @Entity(name = "IdOnly") + @Inheritance(strategy = InheritanceType.JOINED) static class IdOnly { @Id Long id; @@ -142,4 +171,26 @@ static class IdOnly { IdOnly() { } } + + @Entity(name = "IdOnlyIntermediate") + static class IdOnlyIntermediate extends IdOnly { + IdOnlyIntermediate(Long id) { + super( id ); + } + + IdOnlyIntermediate() { + } + } + + @Entity(name = "IdOnlySubtype") + static class IdOnlySubtype extends IdOnlyIntermediate { + String name; + + IdOnlySubtype(Long id) { + super( id ); + } + + IdOnlySubtype() { + } + } } From 6cfdc641535daf0b43f7b75c66b6998164f53f88 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 28 Nov 2025 17:24:39 +0100 Subject: [PATCH 011/181] HHH-19963 Only consider a ToOne be bidirectional for OneToMany if FKs are equal --- .../internal/PluralAttributeMappingImpl.java | 31 ++++- .../BidirectionalOneToManyTest.java | 122 ++++++++++++++++++ 2 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/BidirectionalOneToManyTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java index 09bfb164a96f..b112b114e507 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java @@ -32,6 +32,7 @@ import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SoftDeleteMapping; import org.hibernate.metamodel.mapping.TableDetails; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.metamodel.mapping.ordering.OrderByFragment; import org.hibernate.metamodel.mapping.ordering.OrderByFragmentTranslator; import org.hibernate.metamodel.mapping.ordering.TranslationContext; @@ -225,11 +226,31 @@ private static void injectAttributeMapping( @Override public boolean isBidirectionalAttributeName(NavigablePath fetchablePath, ToOneAttributeMapping modelPart) { return bidirectionalAttributeName == null - // If the FK-target of the to-one mapping is the same as the FK-target of this plural mapping, - // then we say this is bidirectional, given that this is only invoked for model parts of the - // collection elements - ? fkDescriptor.getTargetPart() == modelPart.getForeignKeyDescriptor().getTargetPart() - : fetchablePath.getLocalName().endsWith( bidirectionalAttributeName ); + // If the FK-target of the to-one mapping is the same as the FK-target of this one-to-many mapping, + // and the FK-key refer to the same column then we say this is bidirectional, + // given that this is only invoked for model parts of the collection elements + ? modelPart.getSideNature() == ForeignKeyDescriptor.Nature.KEY + && collectionDescriptor.isOneToMany() + && fkDescriptor.getTargetPart() == modelPart.getForeignKeyDescriptor().getTargetPart() + && areEqual( fkDescriptor.getKeyPart(), modelPart.getForeignKeyDescriptor().getKeyPart() ) + : fetchablePath.getLocalName().equals( bidirectionalAttributeName ); + } + + private boolean areEqual(ValuedModelPart part1, ValuedModelPart part2) { + final int typeCount = part1.getJdbcTypeCount(); + if ( part2.getJdbcTypeCount() != typeCount ) { + return false; + } + for ( int i = 0; i < typeCount; i++ ) { + final SelectableMapping selectable1 = part1.getSelectable( i ); + final SelectableMapping selectable2 = part2.getSelectable( i ); + if ( selectable1.getJdbcMapping() != selectable2.getJdbcMapping() + || !selectable1.getContainingTableExpression().equals( selectable2.getContainingTableExpression() ) + || !selectable1.getSelectionExpression().equals( selectable2.getSelectionExpression() ) ) { + return false; + } + } + return true; } public void finishInitialization( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/BidirectionalOneToManyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/BidirectionalOneToManyTest.java new file mode 100644 index 000000000000..b02d92f30aee --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/collections/BidirectionalOneToManyTest.java @@ -0,0 +1,122 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.mapping.collections; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +@Jpa( annotatedClasses = {BidirectionalOneToManyTest.Organization.class, BidirectionalOneToManyTest.User.class} ) +@Jira("https://hibernate.atlassian.net/browse/HHH-19963") +public class BidirectionalOneToManyTest { + + @Test + public void testParentNotTreatedAsBidirectional(EntityManagerFactoryScope scope) { + scope.inTransaction( entityManager -> { + Organization o3 = new Organization( 3L, "o3", null, new ArrayList<>() ); + Organization o1 = new Organization( 1L, "o1", null, new ArrayList<>( Arrays.asList( o3 )) ); + Organization o2 = new Organization( 2L, "o2", o1, new ArrayList<>() ); + entityManager.persist(o3); + entityManager.persist(o1); + entityManager.persist(o2); + + User u1 = new User( 1L, o2 ); + User u2 = new User( 2L, o2 ); + entityManager.persist(u1); + entityManager.persist(u2); + }); + + scope.inTransaction( entityManager -> { + User user1 = entityManager.find(User.class, 1L); + Organization ou3 = entityManager.find(Organization.class, 3L); + assertNull( ou3.getParentOrganization(), "Parent of o3 is null"); + assertEquals(0, ou3.getPredecessorOrganizations().size(), "Predecessors of o3 is empty"); + }); + } + + @Entity(name = "Organization") + public static class Organization { + + @Id + private Long id; + private String name; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "parentorganization_objectId") + private Organization parentOrganization; + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable(name = "organization_predecessor") + private List predecessorOrganizations = new ArrayList<>(); + + public Organization() { + } + + public Organization(Long id, String name, Organization parentOrganization, List predecessorOrganizations) { + this.id = id; + this.name = name; + this.parentOrganization = parentOrganization; + this.predecessorOrganizations = predecessorOrganizations; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Organization getParentOrganization() { + return parentOrganization; + } + + public List getPredecessorOrganizations() { + return predecessorOrganizations; + } + } + + @Entity(name = "User") + @Table(name = "usr_tbl") + public static class User { + + @Id + private Long id; + @ManyToOne(fetch = FetchType.EAGER) + private Organization organization; + + public User() { + } + + public User(Long id, Organization organization) { + this.id = id; + this.organization = organization; + } + + public Long getId() { + return id; + } + + public Organization getOrganization() { + return organization; + } + } +} From 76d6228173b4f9641101ab164d1710d39275cae5 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 1 Dec 2025 15:04:22 +0100 Subject: [PATCH 012/181] clean up some uses of BOOT_LOGGER --- .../java/org/hibernate/boot/BootLogging.java | 10 ++++++++-- .../org/hibernate/boot/MetadataSources.java | 4 +--- .../boot/archive/internal/ArchiveHelper.java | 4 +++- .../boot/cfgxml/spi/LoadedConfig.java | 11 +++++------ .../boot/internal/BootstrapContextImpl.java | 4 ++-- .../boot/model/internal/AnnotatedColumn.java | 16 +++++++--------- .../boot/model/internal/CollectionBinder.java | 18 +++++++----------- .../boot/model/relational/Namespace.java | 6 +++--- 8 files changed, 36 insertions(+), 37 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/BootLogging.java b/hibernate-core/src/main/java/org/hibernate/boot/BootLogging.java index d827eeeafec8..cb6d1e71fca0 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/BootLogging.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/BootLogging.java @@ -45,7 +45,6 @@ public interface BootLogging extends BasicLogger { @Message(id = 160101, value = "Duplicate generator name: '%s'") void duplicateGeneratorName(String name); - @LogMessage(level = DEBUG) @Message(id = 160111, value = "Package not found or no package-info.java: %s") void packageNotFound(String packageName); @@ -84,7 +83,6 @@ public interface BootLogging extends BasicLogger { @ManyToOne and @OneToOne associations mapped with @NotFound are forced to EAGER fetching""") void ignoreNotFoundWithFetchTypeLazy(String entity, String association); - // --- New typed TRACE/DEBUG messages for boot internals --- @LogMessage(level = TRACE) @Message(id = 160140, value = "Binding formula: %s") void bindingFormula(String formula); @@ -449,4 +447,12 @@ public interface BootLogging extends BasicLogger { @LogMessage(level = DEBUG) @Message(id = 160244, value = "Skipping HBM processing of entity hierarchy [%s], as at least one entity [%s] has been processed") void skippingHbmProcessingOfEntityHierarchy(String rootEntityName, String processedEntity); + + @LogMessage(level = WARN) + @Message(id = 160245, value = "Association '%s' is 'mappedBy' another entity and should not specify a '@MapKeyColumn' (use '@MapKey' instead)") + void mappedByShouldNotSpecifyMapKeyColumn(String associationPath); + + @LogMessage(level = WARN) + @Message(id = 160246, value = "Association '%s' is 'mappedBy' another entity and should not specify an '@OrderColumn' (use '@OrderBy' instead)") + void mappedByShouldNotSpecifyOrderColumn(String associationPath); } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java b/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java index ef46107b6dda..bf6d02c9cd53 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java @@ -105,9 +105,7 @@ public MetadataSources(ServiceRegistry serviceRegistry, XmlMappingBinderAccess x // service registry really should be either BootstrapServiceRegistry or StandardServiceRegistry type... if ( !isExpectedServiceRegistryType( serviceRegistry ) ) { if ( BOOT_LOGGER.isDebugEnabled() ) { - BOOT_LOGGER.unexpectedServiceRegistryType( - serviceRegistry.getClass().getName() - ); + BOOT_LOGGER.unexpectedServiceRegistryType( serviceRegistry.getClass().getName() ); } } this.serviceRegistry = serviceRegistry; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/archive/internal/ArchiveHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/archive/internal/ArchiveHelper.java index 89b2b6cc0ebc..de96e3aad1ef 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/archive/internal/ArchiveHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/archive/internal/ArchiveHelper.java @@ -14,6 +14,8 @@ import org.hibernate.boot.archive.spi.ArchiveException; +import static org.hibernate.boot.BootLogging.BOOT_LOGGER; + /** * Helper for dealing with archives @@ -89,7 +91,7 @@ else if ( "zip".equals( protocol ) "Unable to determine JAR Url from " + url + ". Cause: " + e.getMessage() ); } - org.hibernate.boot.BootLogging.BOOT_LOGGER.jarUrlFromUrlEntry( String.valueOf(url), String.valueOf(jarUrl) ); + BOOT_LOGGER.jarUrlFromUrlEntry( String.valueOf(url), String.valueOf(jarUrl) ); return jarUrl; } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/cfgxml/spi/LoadedConfig.java b/hibernate-core/src/main/java/org/hibernate/boot/cfgxml/spi/LoadedConfig.java index be806d5b7dbb..11b36bd57e78 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/cfgxml/spi/LoadedConfig.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/cfgxml/spi/LoadedConfig.java @@ -100,12 +100,11 @@ public static LoadedConfig consume(JaxbCfgHibernateConfiguration jaxbCfg) { final String eventTypeName = listenerGroup.getType().value(); final var eventType = EventType.resolveEventTypeByName( eventTypeName ); for ( var listener : listenerGroup.getListener() ) { - if ( listener.getType() != null ) { - BOOT_LOGGER.listenerDefinedAlsoDefinedEventType( - listener.getClazz() - ); - } - cfg.addEventListener( eventType, listener.getClazz() ); + final String listenerClassName = listener.getClazz(); + if ( listener.getType() != null ) { + BOOT_LOGGER.listenerDefinedAlsoDefinedEventType( listenerClassName ); + } + cfg.addEventListener( eventType, listenerClassName ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/BootstrapContextImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/BootstrapContextImpl.java index e51dab58de82..c6f6c11295e1 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/BootstrapContextImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/BootstrapContextImpl.java @@ -318,7 +318,7 @@ void injectJpaTempClassLoader(ClassLoader classLoader) { void injectScanOptions(ScanOptions scanOptions) { if ( scanOptions != this.scanOptions ) { - BOOT_LOGGER.injectingScanOptions(scanOptions, this.scanOptions); + BOOT_LOGGER.injectingScanOptions( scanOptions, this.scanOptions ); } this.scanOptions = scanOptions; } @@ -332,7 +332,7 @@ void injectScanEnvironment(ScanEnvironment scanEnvironment) { void injectScanner(Scanner scanner) { if ( scanner != this.scannerSetting ) { - BOOT_LOGGER.injectingScanner( scanner, scannerSetting ); + BOOT_LOGGER.injectingScanner( scanner, this.scannerSetting ); } this.scannerSetting = scanner; } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java index 5ff06ffcb345..2508de98cea1 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java @@ -235,9 +235,7 @@ public AnnotatedColumn() { public void bind() { if ( isNotEmpty( formulaString ) ) { -if ( BOOT_LOGGER.isTraceEnabled() ) { - BOOT_LOGGER.bindingFormula( formulaString ); - } + BOOT_LOGGER.bindingFormula( formulaString ); formula = new Formula(); formula.setFormula( formulaString ); } @@ -269,7 +267,7 @@ public void bind() { if ( generatedAs != null ) { mappingColumn.setGeneratedAs( generatedAs ); } -if ( BOOT_LOGGER.isDebugEnabled() && logicalColumnName != null ) { + if ( logicalColumnName != null ) { BOOT_LOGGER.bindingColumn( logicalColumnName ); } } @@ -770,7 +768,7 @@ private static jakarta.persistence.Column[] overrideColumns( + " columns (every column must have exactly one '@AttributeOverride')" ); } if ( BOOT_LOGGER.isTraceEnabled() ) { - BOOT_LOGGER.columnMappingOverridden( inferredData.getPropertyName() ); + BOOT_LOGGER.columnMappingOverridden( inferredData.getPropertyName() ); } return isEmpty( overriddenCols ) ? null : overriddenCols; } @@ -930,7 +928,7 @@ void applyColumnDefault(PropertyData inferredData, int length) { } } else { -BOOT_LOGGER.couldNotPerformColumnDefaultLookup(); + BOOT_LOGGER.couldNotPerformColumnDefaultLookup(); } } @@ -951,7 +949,7 @@ void applyGeneratedAs(PropertyData inferredData, int length) { } } else { -BOOT_LOGGER.couldNotPerformGeneratedColumnLookup(); + BOOT_LOGGER.couldNotPerformGeneratedColumnLookup(); } } @@ -994,9 +992,9 @@ void applyCheckConstraint(PropertyData inferredData, int length) { } } else { -BOOT_LOGGER.couldNotPerformCheckLookup(); + BOOT_LOGGER.couldNotPerformCheckLookup(); } -} + } //must only be called after all setters are defined and before binding private void extractDataFromPropertyData( diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java index f60ab5e44969..646426742685 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java @@ -1062,9 +1062,7 @@ private void bind() { } collection = createCollection( propertyHolder.getPersistentClass() ); final String role = qualify( propertyHolder.getPath(), propertyName ); - if ( BOOT_LOGGER.isTraceEnabled() ) { - BOOT_LOGGER.bindingCollectionRole( role ); - } + BOOT_LOGGER.bindingCollectionRole( role ); collection.setRole( role ); collection.setMappedByProperty( mappedBy ); @@ -1145,16 +1143,14 @@ private void detectMappedByProblem(boolean isMappedBy) { } if ( oneToMany ) { if ( property.hasDirectAnnotationUsage( MapKeyColumn.class ) ) { - BOOT_LOGGER.warn( "Association '" - + qualify( propertyHolder.getPath(), propertyName ) - + "' is 'mappedBy' another entity and should not specify a '@MapKeyColumn'" - + " (use '@MapKey' instead)" ); + BOOT_LOGGER.mappedByShouldNotSpecifyMapKeyColumn( + qualify( propertyHolder.getPath(), propertyName ) + ); } if ( property.hasDirectAnnotationUsage( OrderColumn.class ) ) { - BOOT_LOGGER.warn( "Association '" - + qualify( propertyHolder.getPath(), propertyName ) - + "' is 'mappedBy' another entity and should not specify an '@OrderColumn'" - + " (use '@OrderBy' instead)" ); + BOOT_LOGGER.mappedByShouldNotSpecifyOrderColumn( + qualify( propertyHolder.getPath(), propertyName ) + ); } } else { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Namespace.java b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Namespace.java index 9df90bc003bf..ccf36577a86c 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Namespace.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Namespace.java @@ -95,9 +95,9 @@ public void registerTable(Identifier logicalName, Table table) { final Table previous = tables.put( logicalName, table ); if ( previous != null ) { BOOT_LOGGER.replacingTableRegistration( - String.valueOf(logicalName), - String.valueOf(previous), - String.valueOf(table) + String.valueOf( logicalName ), + String.valueOf( previous ), + String.valueOf( table ) ); } } From 7ffcd4ff90d98a4e9aa273b88d82249a800604cd Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 1 Dec 2025 20:21:13 +0100 Subject: [PATCH 013/181] miscellaneous minor code cleanups --- .../source/internal/hbm/ModelBinder.java | 79 +++++---- .../spi/interceptor/EnhancementHelper.java | 74 ++++---- .../BytecodeEnhancementMetadataPojoImpl.java | 33 ++-- .../hibernate/engine/spi/CollectionEntry.java | 103 +++++------- .../persister/entity/DirtyHelper.java | 3 - .../org/hibernate/tuple/PropertyFactory.java | 159 ++++++++---------- 6 files changed, 204 insertions(+), 247 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java index 7194d44cf498..d33dcd993745 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java @@ -3239,42 +3239,49 @@ private void bindManyToManyFilters( } for ( var filterSource : elementSource.getFilterSources() ) { - if ( filterSource.getName() == null ) { - if ( BOOT_LOGGER.isTraceEnabled() ) { - BOOT_LOGGER.tracef( - "Encountered filter with no name associated with many-to-many [%s]; skipping", - getPluralAttributeSource().getAttributeRole().getFullPath() - ); - } + bindManyToManyFilter( mappingDocument, collectionBinding, filterSource ); + } + } + + private void bindManyToManyFilter( + MappingDocument mappingDocument, Collection collectionBinding, FilterSource filterSource) { + final String name = filterSource.getName(); + if ( name == null ) { + if ( BOOT_LOGGER.isTraceEnabled() ) { + BOOT_LOGGER.tracef( + "Encountered filter with no name associated with many-to-many [%s]; skipping", + getPluralAttributeSource().getAttributeRole().getFullPath() + ); } - else { - if ( filterSource.getCondition() == null ) { - throw new MappingException( - String.format( - Locale.ENGLISH, - "No filter condition found for filter [%s] associated with many-to-many [%s]", - filterSource.getName(), - getPluralAttributeSource().getAttributeRole().getFullPath() - ), - mappingDocument.getOrigin() - ); - } - if ( BOOT_LOGGER.isTraceEnabled() ) { - BOOT_LOGGER.tracef( - "Applying many-to-many filter [%s] as [%s] to collection [%s]", - filterSource.getName(), - filterSource.getCondition(), - getPluralAttributeSource().getAttributeRole().getFullPath() - ); - } - collectionBinding.addManyToManyFilter( - filterSource.getName(), - filterSource.getCondition(), - filterSource.shouldAutoInjectAliases(), - filterSource.getAliasToTableMap(), - filterSource.getAliasToEntityMap() + } + else { + final String condition = filterSource.getCondition(); + if ( condition == null ) { + throw new MappingException( + String.format( + Locale.ENGLISH, + "No filter condition found for filter [%s] associated with many-to-many [%s]", + name, + getPluralAttributeSource().getAttributeRole().getFullPath() + ), + mappingDocument.getOrigin() ); } + if ( BOOT_LOGGER.isTraceEnabled() ) { + BOOT_LOGGER.tracef( + "Applying many-to-many filter [%s] as [%s] to collection [%s]", + name, + condition, + getPluralAttributeSource().getAttributeRole().getFullPath() + ); + } + collectionBinding.addManyToManyFilter( + name, + condition, + filterSource.shouldAutoInjectAliases(), + filterSource.getAliasToTableMap(), + filterSource.getAliasToEntityMap() + ); } } @@ -3451,10 +3458,9 @@ protected void createBackReferences() { && !collectionBinding.isInverse() && !indexIsFormula ) { final var oneToMany = (OneToMany) collectionBinding.getElement(); - final String entityName = oneToMany.getReferencedEntityName(); final var referenced = getMappingDocument().getMetadataCollector() - .getEntityBinding( entityName ); + .getEntityBinding( oneToMany.getReferencedEntityName() ); final var backref = new IndexBackref(); backref.setName( '_' + collectionBinding.getOwnerEntityName() + "." + getPluralAttributeSource().getName() + "IndexBackref" ); @@ -3533,10 +3539,9 @@ private void createIndexBackRef( && !collectionBinding.getKey().isNullable() && !collectionBinding.isInverse() ) { final var oneToMany = (OneToMany) collectionBinding.getElement(); - final String entityName = oneToMany.getReferencedEntityName(); final var referenced = mappingDocument.getMetadataCollector() - .getEntityBinding( entityName ); + .getEntityBinding( oneToMany.getReferencedEntityName() ); final var backref = new IndexBackref(); backref.setName( '_' + collectionBinding.getOwnerEntityName() + "." + pluralAttributeSource.getName() + "IndexBackref" ); diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementHelper.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementHelper.java index 2b072677721c..66920b662393 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementHelper.java @@ -32,28 +32,26 @@ public interface InheritanceChecker { * Should the given property be included in the owner's base fetch group? */ public static boolean includeInBaseFetchGroup( - Property bootMapping, + Property property, boolean isEnhanced, InheritanceChecker inheritanceChecker, boolean collectionsInDefaultFetchGroupEnabled) { - final var value = bootMapping.getValue(); - - if ( ! isEnhanced ) { - if ( value instanceof ToOne toOne ) { - if ( toOne.isUnwrapProxy() ) { - BYTECODE_INTERCEPTOR_LOGGER.toOneLazyNoProxyButNotEnhanced( - bootMapping.getPersistentClass().getEntityName(), - bootMapping.getName() - ); - } + final var value = property.getValue(); + + if ( !isEnhanced ) { + if ( value instanceof ToOne toOne && toOne.isUnwrapProxy() ) { + BYTECODE_INTERCEPTOR_LOGGER.toOneLazyNoProxyButNotEnhanced( + property.getPersistentClass().getEntityName(), + property.getName() + ); } return true; } // if we get here, we know the property owner is enhanced for laziness // - // NOTE : we make the (potentially untrue) assumption here that - // if the owner is enhanced, then all classes are enhanced.. + // NOTE: we make the (potentially untrue) assumption here that + // if the owner is enhanced, then all classes are enhanced. if ( value instanceof ToOne toOne ) { @@ -64,7 +62,7 @@ public static boolean includeInBaseFetchGroup( // it is lazy. see if we should select the FK - if ( bootMapping.getLazyGroup() != null ) { + if ( property.getLazyGroup() != null ) { // a non-base fetch group was explicitly specified // // really this should indicate to not select it as part of the base group. @@ -72,9 +70,9 @@ public static boolean includeInBaseFetchGroup( // we simply log a message that we are ignoring the `@LazyGroup` for to-ones BYTECODE_INTERCEPTOR_LOGGER.lazyGroupIgnoredForToOne( - bootMapping.getLazyGroup(), - bootMapping.getPersistentClass().getEntityName(), - bootMapping.getName() + property.getLazyGroup(), + property.getPersistentClass().getEntityName(), + property.getName() ); // at a later time - for example 6.0 when we can implement the join solution @@ -84,13 +82,13 @@ public static boolean includeInBaseFetchGroup( // for now, fall through } - if ( ! toOne.isReferenceToPrimaryKey() ) { + if ( !toOne.isReferenceToPrimaryKey() ) { // we do not have a reference to the associated primary-key return false; } if ( toOne.getColumnSpan() == 0 ) { - // generally this would indicate a "shared PK" on-to-one and there + // generally this would indicate a "shared PK" one-to-one and there // is no column for the association on the owner table - do not add // the association to the base group (which would force an immediate // select from the association table effectively making this @@ -98,15 +96,16 @@ public static boolean includeInBaseFetchGroup( return false; } - final boolean unwrapExplicitlyRequested = toOne.isUnwrapProxy() && !toOne.isUnwrapProxyImplicit(); + final boolean unwrapExplicitlyRequested = + toOne.isUnwrapProxy() && !toOne.isUnwrapProxyImplicit(); if ( inheritanceChecker.hasSubclasses( toOne.getReferencedEntityName() ) ) { // the associated type has subclasses - we cannot use the enhanced proxy and will generate a HibernateProxy if ( unwrapExplicitlyRequested ) { // NO_PROXY was explicitly requested BYTECODE_INTERCEPTOR_LOGGER.lazyNoProxyButAssociatedHasSubclasses( - bootMapping.getPersistentClass().getEntityName(), - bootMapping.getName(), + property.getPersistentClass().getEntityName(), + property.getName(), toOne.getReferencedEntityName() ); } @@ -117,8 +116,8 @@ public static boolean includeInBaseFetchGroup( if ( toOne instanceof ManyToOne manyToOne && manyToOne.isIgnoreNotFound() ) { if ( unwrapExplicitlyRequested ) { BYTECODE_INTERCEPTOR_LOGGER.notFoundIgnoreWithNoProxySkippingFkSelection( - bootMapping.getPersistentClass().getEntityName(), - bootMapping.getName() + property.getPersistentClass().getEntityName(), + property.getName() ); return false; } @@ -135,7 +134,7 @@ public static boolean includeInBaseFetchGroup( } return collectionsInDefaultFetchGroupEnabled && ( value instanceof Collection ) - || ! bootMapping.isLazy(); + || ! property.isLazy(); } public static T performWork( @@ -143,7 +142,7 @@ public static T performWork( BiFunction work, String entityName, String attributeName) { - SharedSessionContractImplementor session = interceptor.getLinkedSession(); + var session = interceptor.getLinkedSession(); final boolean isTempSession; final boolean isJta; @@ -236,23 +235,20 @@ enum Cause { NO_SF_UUID } - private static LazyInitializationException createLazyInitializationException(final Cause cause, final String entityName, final String attributeName) { - final String reason = switch ( cause ) { - case NO_SESSION -> "no session and settings disallow loading outside the Session"; - case CLOSED_SESSION -> "session is closed and settings disallow loading outside the Session"; - case DISCONNECTED_SESSION -> "session is disconnected and settings disallow loading outside the Session"; - case NO_SF_UUID -> "could not determine SessionFactory UUId to create temporary Session for loading"; - }; - - final String message = String.format( + private static LazyInitializationException createLazyInitializationException( + Cause cause, String entityName, String attributeName) { + return new LazyInitializationException( String.format( Locale.ROOT, "Unable to perform requested lazy initialization [%s.%s] - %s", entityName, attributeName, - reason - ); - - return new LazyInitializationException( message ); + switch ( cause ) { + case NO_SESSION -> "no session and settings disallow loading outside the Session"; + case CLOSED_SESSION -> "session is closed and settings disallow loading outside the Session"; + case DISCONNECTED_SESSION -> "session is disconnected and settings disallow loading outside the Session"; + case NO_SF_UUID -> "could not determine SessionFactory UUId to create temporary Session for loading"; + } + ) ); } private static SharedSessionContractImplementor openTemporarySessionForLoading( diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeEnhancementMetadataPojoImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeEnhancementMetadataPojoImpl.java index c8536e8792b0..10677b67feb1 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeEnhancementMetadataPojoImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeEnhancementMetadataPojoImpl.java @@ -14,18 +14,14 @@ import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributesMetadata; import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata; import org.hibernate.bytecode.spi.NotInstrumentedException; -import org.hibernate.engine.spi.EntityEntry; -import org.hibernate.engine.spi.EntityHolder; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.ManagedEntity; -import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistentAttributeInterceptable; import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.engine.spi.SelfDirtinessTracker; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.Status; import org.hibernate.mapping.PersistentClass; -import org.hibernate.persister.entity.EntityPersister; import org.hibernate.type.CompositeType; import org.checkerframework.checker.nullness.qual.Nullable; @@ -50,9 +46,9 @@ public static BytecodeEnhancementMetadataPojoImpl from( CompositeType nonAggregatedCidMapper, boolean collectionsInDefaultFetchGroupEnabled, Metadata metadata) { - final Class mappedClass = persistentClass.getMappedClass(); + final var mappedClass = persistentClass.getMappedClass(); final boolean enhancedForLazyLoading = isPersistentAttributeInterceptableType( mappedClass ); - final LazyAttributesMetadata lazyAttributesMetadata = enhancedForLazyLoading + final var lazyAttributesMetadata = enhancedForLazyLoading ? LazyAttributesMetadata.from( persistentClass, true, collectionsInDefaultFetchGroupEnabled, metadata ) : LazyAttributesMetadata.nonEnhanced( persistentClass.getEntityName() ); @@ -139,7 +135,7 @@ public boolean isAttributeLoaded(Object entity, String attributeName) { return true; } - final BytecodeLazyAttributeInterceptor interceptor = extractLazyInterceptor( entity ); + final var interceptor = extractLazyInterceptor( entity ); if ( interceptor instanceof LazyAttributeLoadingInterceptor ) { return interceptor.isAttributeLoaded( attributeName ); } @@ -154,12 +150,12 @@ public boolean isAttributeLoaded(Object entity, String attributeName) { @Override public PersistentAttributeInterceptable createEnhancedProxy(EntityKey entityKey, boolean addEmptyEntry, SharedSessionContractImplementor session) { - final EntityPersister persister = entityKey.getPersister(); + final var persister = entityKey.getPersister(); final Object identifier = entityKey.getIdentifier(); - final PersistenceContext persistenceContext = session.getPersistenceContext(); + final var persistenceContext = session.getPersistenceContext(); // first, instantiate the entity instance to use as the proxy - final PersistentAttributeInterceptable entity = asPersistentAttributeInterceptable( persister.instantiate( identifier, session ) ); + final var entity = asPersistentAttributeInterceptable( persister.instantiate( identifier, session ) ); // clear the fields that are marked as dirty in the dirtiness tracker processIfSelfDirtinessTracker( entity, BytecodeEnhancementMetadataPojoImpl::clearDirtyAttributes ); @@ -170,8 +166,8 @@ public PersistentAttributeInterceptable createEnhancedProxy(EntityKey entityKey, // if requested, add the "holder entry" to the PC if ( addEmptyEntry ) { - EntityHolder entityHolder = persistenceContext.getEntityHolder( entityKey ); - EntityEntry entityEntry = persistenceContext.addEntry( + final var entityHolder = persistenceContext.getEntityHolder( entityKey ); + final var entityEntry = persistenceContext.addEntry( entity, Status.MANAGED, // loaded state @@ -223,14 +219,12 @@ public LazyAttributeLoadingInterceptor injectInterceptor( ) ); } - final LazyAttributeLoadingInterceptor interceptor = new LazyAttributeLoadingInterceptor( - this.lazyAttributeLoadingInterceptorState, + final var interceptor = new LazyAttributeLoadingInterceptor( + lazyAttributeLoadingInterceptorState, identifier, session ); - injectInterceptor( entity, interceptor, session ); - return interceptor; } @@ -257,10 +251,11 @@ public void injectEnhancedEntityAsProxyInterceptor( //This state object needs to be lazily initialized as it needs access to the Persister, but once //initialized it can be reused across multiple sessions. public EnhancementAsProxyLazinessInterceptor.EntityRelatedState getEnhancementAsProxyLazinessInterceptorMetastate(SharedSessionContractImplementor session) { - EnhancementAsProxyLazinessInterceptor.EntityRelatedState state = this.enhancementAsProxyInterceptorState; + var state = this.enhancementAsProxyInterceptorState; if ( state == null ) { - final EntityPersister entityPersister = session.getFactory().getMappingMetamodel() - .getEntityDescriptor( entityName ); + final var entityPersister = + session.getFactory().getMappingMetamodel() + .getEntityDescriptor( entityName ); state = new EnhancementAsProxyLazinessInterceptor.EntityRelatedState( entityPersister, nonAggregatedCidMapper, 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 f6981b79b4f3..f3ad3d7c9149 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 @@ -65,11 +65,9 @@ public CollectionEntry(CollectionPersister persister, PersistentCollection co // new collections that get found + wrapped // during flush shouldn't be ignored ignore = false; - // a newly wrapped collection is NOT dirty // (or we get unnecessary version updates) collection.clearDirty(); - snapshot = persister.isMutable() ? collection.getSnapshot( persister ) : null; role = persister.getRole(); collection.setSnapshot( loadedKey, role, snapshot ); @@ -84,16 +82,11 @@ public CollectionEntry( final Object loadedKey, final boolean ignore ) { this.ignore = ignore; - //collection.clearDirty() - this.loadedKey = loadedKey; - this.loadedPersister = loadedPersister; this.role = loadedPersister == null ? null : loadedPersister.getRole(); - collection.setSnapshot( loadedKey, role, null ); - //postInitialize() will be called after initialization } @@ -104,12 +97,10 @@ public CollectionEntry(CollectionPersister loadedPersister, Object loadedKey) { // detached collection wrappers that get found + reattached // during flush shouldn't be ignored ignore = false; - //collection.clearDirty() - this.loadedKey = loadedKey; this.loadedPersister = loadedPersister; - this.role = ( loadedPersister == null ? null : loadedPersister.getRole() ); + this.role = loadedPersister == null ? null : loadedPersister.getRole(); } /** @@ -119,11 +110,11 @@ public CollectionEntry(PersistentCollection collection, SessionFactoryImpleme // detached collections that get found + reattached // during flush shouldn't be ignored ignore = false; - loadedKey = collection.getKey(); role = collection.getRole(); - loadedPersister = factory.getMappingMetamodel().getCollectionDescriptor( castNonNull( role ) ); - + loadedPersister = + factory.getMappingMetamodel() + .getCollectionDescriptor( castNonNull( role ) ); snapshot = collection.getStoredSnapshot(); } @@ -151,53 +142,52 @@ private CollectionEntry( * of the collection elements, if necessary */ private void dirty(PersistentCollection collection) { - - final var loadedPersister = getLoadedPersister(); - final boolean forceDirty = - collection.wasInitialized() - && !collection.isDirty() //optimization - && loadedPersister != null - && loadedPersister.isMutable() //optimization - && ( collection.isDirectlyAccessible() || loadedPersister.getElementType().isMutable() ) //optimization - && !collection.equalsSnapshot( loadedPersister ); - if ( forceDirty ) { + if ( forceDirty( collection ) ) { collection.dirty(); } + } + private boolean forceDirty(PersistentCollection collection) { + final var loadedPersister = this.loadedPersister; + return collection.wasInitialized() + && !collection.isDirty() //optimization + && loadedPersister != null + && loadedPersister.isMutable() //optimization + && ( collection.isDirectlyAccessible() || loadedPersister.getElementType().isMutable() ) //optimization + && !collection.equalsSnapshot( loadedPersister ); } public void preFlush(PersistentCollection collection) { - if ( loadedKey == null && collection.getKey() != null ) { + if ( loadedKey == null ) { loadedKey = collection.getKey(); } - final var loadedPersister = getLoadedPersister(); - final boolean nonMutableChange = - collection.isDirty() - && loadedPersister != null - && !loadedPersister.isMutable(); - if ( nonMutableChange ) { + final var loadedPersister = this.loadedPersister; + if ( collection.isDirty() + && loadedPersister != null + && !loadedPersister.isMutable() ) { throw new HibernateException( "Immutable collection was modified: " + - collectionInfoString( castNonNull( loadedPersister ).getRole(), getLoadedKey() ) ); + collectionInfoString( loadedPersister.getRole(), getLoadedKey() ) ); } dirty( collection ); - if ( CORE_LOGGER.isTraceEnabled() && collection.isDirty() && loadedPersister != null ) { + if ( CORE_LOGGER.isTraceEnabled() + && loadedPersister != null + && collection.isDirty() ) { CORE_LOGGER.collectionDirty( collectionInfoString( loadedPersister.getRole(), getLoadedKey() ) ); } - setReached( false ); - setProcessed( false ); - - setDoupdate( false ); - setDoremove( false ); - setDorecreate( false ); + reached = false; + processed = false; + doupdate = false; + doremove = false; + dorecreate = false; } public void postInitialize(PersistentCollection collection, SharedSessionContractImplementor session) { - final var loadedPersister = getLoadedPersister(); + final var loadedPersister = this.loadedPersister; snapshot = loadedPersister != null && loadedPersister.isMutable() ? collection.getSnapshot( loadedPersister ) @@ -214,13 +204,11 @@ public void postInitialize(PersistentCollection collection, SharedSessionCont * Called after a successful flush */ public void postFlush(PersistentCollection collection) { - if ( isIgnore() ) { - ignore = false; - } - else if ( !isProcessed() ) { + if ( !ignore && !processed ) { throw new HibernateException( "Collection '" + collection.getRole() + "' was not processed by flush" + " (this is likely due to unsafe use of the session, for example, current use in multiple threads, or updates during entity lifecycle callbacks)"); } + ignore = false; collection.setSnapshot( loadedKey, role, snapshot ); } @@ -228,18 +216,17 @@ else if ( !isProcessed() ) { * Called after execution of an action */ public void afterAction(PersistentCollection collection) { - loadedKey = getCurrentKey(); - setLoadedPersister( getCurrentPersister() ); - + loadedKey = currentKey; + loadedPersister = currentPersister; + role = currentPersister == null ? null : currentPersister.getRole(); if ( collection.wasInitialized() - && ( isDoremove() || isDorecreate() || isDoupdate() ) ) { + && ( doremove || dorecreate || doupdate ) ) { // update the snapshot snapshot = loadedPersister != null && loadedPersister.isMutable() ? collection.getSnapshot( castNonNull( loadedPersister ) ) : null; } - collection.postAction(); } @@ -266,7 +253,6 @@ public void afterAction(PersistentCollection collection) { */ public void resetStoredSnapshot(PersistentCollection collection, Serializable storedSnapshot) { CORE_LOGGER.resetStoredSnapshot( storedSnapshot, this ); - if ( !fromMerge ) { snapshot = storedSnapshot; collection.setSnapshot( loadedKey, role, snapshot ); @@ -274,11 +260,6 @@ public void resetStoredSnapshot(PersistentCollection collection, Serializable } } - private void setLoadedPersister(@Nullable CollectionPersister persister) { - loadedPersister = persister; - setRole( persister == null ? null : persister.getRole() ); - } - void afterDeserialize(@Nullable SessionFactoryImplementor factory) { loadedPersister = factory == null ? null : factory.getMappingMetamodel() @@ -286,7 +267,7 @@ void afterDeserialize(@Nullable SessionFactoryImplementor factory) { } public boolean wasDereferenced() { - return getLoadedKey() == null; + return loadedKey == null; } public boolean isReached() { @@ -370,13 +351,13 @@ public void setRole(@Nullable String role) { @Override public String toString() { - final StringBuilder result = + final var result = new StringBuilder( "CollectionEntry" ) .append( collectionInfoString( role, loadedKey ) ); - final CollectionPersister persister = currentPersister; - if ( persister != null ) { + final var currentPersister = this.currentPersister; + if ( currentPersister != null ) { result.append( "->" ) - .append( collectionInfoString( persister.getRole(), currentKey ) ); + .append( collectionInfoString( currentPersister.getRole(), currentKey ) ); } return result.toString(); } @@ -395,11 +376,9 @@ public boolean isSnapshotEmpty(PersistentCollection collection) { //TODO: does this really need to be here? // does the collection already have // it's own up-to-date snapshot? - final var loadedPersister = getLoadedPersister(); - final Serializable snapshot = getSnapshot(); return collection.wasInitialized() && ( loadedPersister == null || loadedPersister.isMutable() ) - && ( snapshot == null || collection.isSnapshotEmpty(snapshot) ); + && ( snapshot == null || collection.isSnapshotEmpty( snapshot ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/DirtyHelper.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/DirtyHelper.java index d8eb504ad3f6..9819b2cdb036 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/DirtyHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/DirtyHelper.java @@ -96,9 +96,7 @@ public static int[] findDirty( int[] results = null; int count = 0; int span = propertyTypes.length; - for ( int i = 0; i < span; i++ ) { - if ( isDirty( propertyTypes, currentState, previousState, includeColumns, session, i ) ) { if ( results == null ) { results = new int[span]; @@ -106,7 +104,6 @@ public static int[] findDirty( results[count++] = i; } } - return count == 0 ? null : ArrayHelper.trim( results, count ); } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java b/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java index ddaa61cdb263..79fe41043f8c 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java @@ -4,10 +4,6 @@ */ package org.hibernate.tuple; -import org.hibernate.HibernateException; -import org.hibernate.boot.spi.MetadataImplementor; -import org.hibernate.boot.spi.SessionFactoryOptions; -import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.generator.Generator; import org.hibernate.mapping.PersistentClass; @@ -26,6 +22,8 @@ import org.hibernate.type.EntityType; import org.hibernate.type.Type; +import static org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper.includeInBaseFetchGroup; + /** * @deprecated No direct replacement */ @@ -45,9 +43,8 @@ private PropertyFactory() { public static IdentifierProperty buildIdentifierAttribute( PersistentClass mappedEntity, Generator generator) { - Type type = mappedEntity.getIdentifier().getType(); - Property property = mappedEntity.getIdentifierProperty(); - + final var type = mappedEntity.getIdentifier().getType(); + final var property = mappedEntity.getIdentifierProperty(); if ( property == null ) { // this is a virtual id property... return new IdentifierProperty( @@ -82,9 +79,7 @@ public static VersionProperty buildVersionProperty( int attributeNumber, Property property, boolean lazyAvailable) { - - boolean lazy = lazyAvailable && property.isLazy(); - + final boolean lazy = lazyAvailable && property.isLazy(); return new VersionProperty( persister, sessionFactory, @@ -127,9 +122,7 @@ public static NonIdentifierAttribute buildEntityBasedAttribute( Property property, boolean lazyAvailable, RuntimeModelCreationContext creationContext) { - final Type type = property.getValue().getType(); - - final NonIdentifierAttributeNature nature = decode( type ); + final var type = property.getValue().getType(); // we need to dirty check collections, since they can cause an owner // version number increment @@ -138,89 +131,82 @@ public static NonIdentifierAttribute buildEntityBasedAttribute( // to update the cache (not the database), since in this case a null // entity reference can lose information - boolean alwaysDirtyCheck = type.isAssociationType() + final boolean alwaysDirtyCheck = type.isAssociationType() && ( (AssociationType) type ).isAlwaysDirtyChecked(); - SessionFactoryOptions sessionFactoryOptions = sessionFactory.getSessionFactoryOptions(); - final boolean lazy = ! EnhancementHelper.includeInBaseFetchGroup( + final boolean lazy = !includeInBaseFetchGroup( property, lazyAvailable, entityName -> { - final MetadataImplementor metadata = creationContext.getMetadata(); - final PersistentClass entityBinding = metadata.getEntityBinding( entityName ); + final var entityBinding = + creationContext.getMetadata() + .getEntityBinding( entityName ); assert entityBinding != null; return entityBinding.hasSubclasses(); }, - sessionFactoryOptions.isCollectionsInDefaultFetchGroupEnabled() + sessionFactory.getSessionFactoryOptions() + .isCollectionsInDefaultFetchGroupEnabled() ); - switch ( nature ) { - case BASIC: { - return new EntityBasedBasicAttribute( - persister, - sessionFactory, - attributeNumber, - property.getName(), - type, - new BaselineAttributeInformation.Builder() - .setLazy( lazy ) - .setInsertable( property.isInsertable() ) - .setUpdateable( property.isUpdatable() ) - .setNullable( property.isOptional() ) - .setDirtyCheckable( alwaysDirtyCheck || property.isUpdatable() ) - .setVersionable( property.isOptimisticLocked() ) - .setCascadeStyle( property.getCascadeStyle() ) - .setOnDeleteAction( property.getOnDeleteAction() ) - .setFetchMode( property.getValue().getFetchMode() ) - .createInformation() - ); - } - case COMPOSITE: { - return new EntityBasedCompositionAttribute( - persister, - sessionFactory, - attributeNumber, - property.getName(), - (CompositeType) type, - new BaselineAttributeInformation.Builder() - .setLazy( lazy ) - .setInsertable( property.isInsertable() ) - .setUpdateable( property.isUpdatable() ) - .setNullable( property.isOptional() ) - .setDirtyCheckable( alwaysDirtyCheck || property.isUpdatable() ) - .setVersionable( property.isOptimisticLocked() ) - .setCascadeStyle( property.getCascadeStyle() ) - .setOnDeleteAction( property.getOnDeleteAction() ) - .setFetchMode( property.getValue().getFetchMode() ) - .createInformation() - ); - } - case ENTITY: - case ANY: - case COLLECTION: { - return new EntityBasedAssociationAttribute( - persister, - sessionFactory, - attributeNumber, - property.getName(), - (AssociationType) type, - new BaselineAttributeInformation.Builder() - .setLazy( lazy ) - .setInsertable( property.isInsertable() ) - .setUpdateable( property.isUpdatable() ) - .setNullable( property.isOptional() ) - .setDirtyCheckable( alwaysDirtyCheck || property.isUpdatable() ) - .setVersionable( property.isOptimisticLocked() ) - .setCascadeStyle( property.getCascadeStyle() ) - .setOnDeleteAction( property.getOnDeleteAction() ) - .setFetchMode( property.getValue().getFetchMode() ) - .createInformation() - ); - } - default: { - throw new HibernateException( "Internal error" ); - } - } + return switch ( decode( type ) ) { + case BASIC -> + new EntityBasedBasicAttribute( + persister, + sessionFactory, + attributeNumber, + property.getName(), + type, + new BaselineAttributeInformation.Builder() + .setLazy( lazy ) + .setInsertable( property.isInsertable() ) + .setUpdateable( property.isUpdatable() ) + .setNullable( property.isOptional() ) + .setDirtyCheckable( alwaysDirtyCheck || property.isUpdatable() ) + .setVersionable( property.isOptimisticLocked() ) + .setCascadeStyle( property.getCascadeStyle() ) + .setOnDeleteAction( property.getOnDeleteAction() ) + .setFetchMode( property.getValue().getFetchMode() ) + .createInformation() + ); + case COMPOSITE -> + new EntityBasedCompositionAttribute( + persister, + sessionFactory, + attributeNumber, + property.getName(), + (CompositeType) type, + new BaselineAttributeInformation.Builder() + .setLazy( lazy ) + .setInsertable( property.isInsertable() ) + .setUpdateable( property.isUpdatable() ) + .setNullable( property.isOptional() ) + .setDirtyCheckable( alwaysDirtyCheck || property.isUpdatable() ) + .setVersionable( property.isOptimisticLocked() ) + .setCascadeStyle( property.getCascadeStyle() ) + .setOnDeleteAction( property.getOnDeleteAction() ) + .setFetchMode( property.getValue().getFetchMode() ) + .createInformation() + ); + case ENTITY, ANY, COLLECTION -> + new EntityBasedAssociationAttribute( + persister, + sessionFactory, + attributeNumber, + property.getName(), + (AssociationType) type, + new BaselineAttributeInformation.Builder() + .setLazy( lazy ) + .setInsertable( property.isInsertable() ) + .setUpdateable( property.isUpdatable() ) + .setNullable( property.isOptional() ) + .setDirtyCheckable( alwaysDirtyCheck || property.isUpdatable() ) + .setVersionable( property.isOptimisticLocked() ) + .setCascadeStyle( property.getCascadeStyle() ) + .setOnDeleteAction( property.getOnDeleteAction() ) + .setFetchMode( property.getValue().getFetchMode() ) + .createInformation() + ); + }; } private static NonIdentifierAttributeNature decode(Type type) { @@ -240,5 +226,4 @@ else if ( type instanceof ComponentType ) { return NonIdentifierAttributeNature.BASIC; } } - } From d100907efc09db602126c539555bbb9bd6c50cd7 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 1 Dec 2025 22:32:38 +0100 Subject: [PATCH 014/181] add item to SchemaManager jdoc --- .../src/main/java/org/hibernate/relational/SchemaManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/relational/SchemaManager.java b/hibernate-core/src/main/java/org/hibernate/relational/SchemaManager.java index 395b82a12bb0..8df8b1326eac 100644 --- a/hibernate-core/src/main/java/org/hibernate/relational/SchemaManager.java +++ b/hibernate-core/src/main/java/org/hibernate/relational/SchemaManager.java @@ -10,7 +10,8 @@ /** * Allows programmatic {@linkplain #exportMappedObjects schema export}, * {@linkplain #validateMappedObjects schema validation}, - * {@linkplain #truncateMappedObjects data cleanup}, and + * {@linkplain #truncateMappedObjects data cleanup}, + * {@linkplain #populate data population}, and * {@linkplain #dropMappedObjects schema cleanup} as a convenience for * writing tests. * From 8631bb216131c0396b31255022ed7e1a14d7443a Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 2 Dec 2025 10:23:36 +0100 Subject: [PATCH 015/181] HHH-19965 remove misleading examples from the User Guide @OrderColumn is like @MapKeyColumn. It's not supposed to be used on the unowned side of an association. --- .../main/asciidoc/introduction/Advanced.adoc | 11 ++++ .../chapters/domain/collections.adoc | 52 ++----------------- 2 files changed, 14 insertions(+), 49 deletions(-) diff --git a/documentation/src/main/asciidoc/introduction/Advanced.adoc b/documentation/src/main/asciidoc/introduction/Advanced.adoc index e874f867d68d..f9e6b165aabf 100644 --- a/documentation/src/main/asciidoc/introduction/Advanced.adoc +++ b/documentation/src/main/asciidoc/introduction/Advanced.adoc @@ -886,6 +886,17 @@ On the other hand, the following annotations specify how a collection should be Under the covers, Hibernate uses a `TreeSet` or `TreeMap` to maintain the collection in sorted order. +[CAUTION] +==== +The unowned (`mappedBy`) side of a bidirectional association is not responsible for specifying column mappings. +So it's wrong in principle to use `@OrderColumn` or `@MapKeyColumn` on the unowned side of an association mapping. +But for unowned collections, we may use `@OrderBy` or `@MapKey` instead. +That is: + +- You can use `@OrderColumn` or `@MapKeyColumn` with an `@ElementCollection`, owned `@ManyToMany`, or owned `@OneToMany`. +- But use `@OrderBy` or `@MapKey` when it's an _unowned_ `@ManyToMany` or `@OneToMany`. +==== + [[any]] === Any mappings diff --git a/documentation/src/main/asciidoc/userguide/chapters/domain/collections.adoc b/documentation/src/main/asciidoc/userguide/chapters/domain/collections.adoc index c59f24821898..6ab1267c5c6e 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/domain/collections.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/domain/collections.adoc @@ -683,7 +683,9 @@ include::{extrasdir}/collections-unidirectional-ordered-list-order-column-select ---- ==== -With the `order_id` column in place, Hibernate can order the list in-memory after it's being fetched from the database. +With the `order_id` column in place, Hibernate can order the list in-memory after it's fetched from the database. + +The <> may be used to choose between 0-based and 1-based indexing on the database side. [[collections-bidirectional-ordered-list]] ===== Bidirectional ordered lists @@ -701,54 +703,6 @@ include::{example-dir-collection}/BidirectionalOrderByListTest.java[tags=collect Just like with the unidirectional `@OrderBy` list, the `number` column is used to order the statement on the SQL level. -When using the `@OrderColumn` annotation, the `order_id` column is going to be embedded in the child table: - -[[collections-bidirectional-ordered-list-order-column-example]] -.Bidirectional `@OrderColumn` list -==== -[source,java] ----- -include::{example-dir-collection}/BidirectionalOrderColumnListTest.java[tags=collections-bidirectional-ordered-list-order-column-example,indent=0] ----- - -[source,sql] ----- -include::{extrasdir}/collections-bidirectional-ordered-list-order-column-example.sql[] ----- -==== - -When fetching the collection, Hibernate will use the fetched ordered columns to sort the elements according to the `@OrderColumn` mapping. - -[[collections-customizing-ordered-list-ordinal]] -===== Customizing ordered list ordinal - -You can customize the ordinal of the underlying ordered list by using the https://docs.hibernate.org/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/ListIndexBase.html[`@ListIndexBase`] annotation. - -[[collections-customizing-ordered-list-ordinal-mapping-example]] -.`@ListIndexBase` mapping example -==== -[source,java] ----- -include::{example-dir-collection}/OrderColumnListIndexBaseTest.java[tags=collections-customizing-ordered-list-ordinal-mapping-example,indent=0] ----- -==== - -When inserting two `Phone` records, Hibernate is going to start the List index from 100 this time. - -[[collections-customizing-ordered-list-ordinal-persist-example]] -.`@ListIndexBase` persist example -==== -[source,java] ----- -include::{example-dir-collection}/OrderColumnListIndexBaseTest.java[tags=collections-customizing-ordered-list-ordinal-persist-example,indent=0] ----- - -[source,sql] ----- -include::{extrasdir}/collections-customizing-ordered-list-ordinal-persist-example.sql[] ----- -==== - [[collections-customizing-ordered-by-sql-clause]] ===== Customizing ORDER BY SQL clause From eb898b125e01e7859347c1eab7fb921d9d983da6 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Thu, 13 Nov 2025 13:36:48 +0100 Subject: [PATCH 016/181] HHH-19926 add tests --- .../hibernate/orm/test/query/hql/FunctionTests.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java index 4f05ed67cb75..9b1ee6a76e41 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java @@ -2492,6 +2492,18 @@ public void testIn(SessionFactoryScope scope) { session.createQuery("select 1 where 1 in (:list)", Integer.class) .setParameterList("list",List.of()) .list().size() ); + assertEquals( 1, + session.createQuery("select 1 where 1 in (:list)", Integer.class) + .setParameter("list",List.of(1)) + .list().size() ); + assertEquals( 0, + session.createQuery("select 1 where 1 in (:list)", Integer.class) + .setParameter("list",List.of()) + .list().size() ); + assertEquals( 0, + session.createQuery("select 1 where 1 in (:list)", Integer.class) + .setParameter("list",null) + .list().size() ); assertEquals( 1, session.createQuery("select 1 where 1 in :list", Integer.class) .setParameterList("list",List.of(1,2)) From fee7ed7ee84a9e3acc2c9b2e36e9aa7063feea9d Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 3 Dec 2025 10:16:40 +0100 Subject: [PATCH 017/181] minor cleanups to BeanValidationEventListener --- .../BeanValidationEventListener.java | 76 +++++++++++-------- .../beanvalidation/TypeSafeActivator.java | 10 +-- 2 files changed, 48 insertions(+), 38 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationEventListener.java b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationEventListener.java index 42735bf68f4d..79b845e300a3 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationEventListener.java @@ -54,20 +54,24 @@ public class BeanValidationEventListener private SessionFactoryImplementor sessionFactory; - public BeanValidationEventListener(ValidatorFactory factory, Map settings, ClassLoaderService classLoaderService) { + public BeanValidationEventListener( + ValidatorFactory factory, Map settings, ClassLoaderService classLoaderService) { traversableResolver = new HibernateTraversableResolver(); validator = factory.usingContext() .traversableResolver( traversableResolver ) .getValidator(); - groupsPerOperation = GroupsPerOperation.from( settings, new ClassLoaderAccessImpl( classLoaderService ) ); + groupsPerOperation = + GroupsPerOperation.from( settings, + new ClassLoaderAccessImpl( classLoaderService ) ); } @Override public void sessionFactoryCreated(SessionFactory factory) { sessionFactory = factory.unwrap( SessionFactoryImplementor.class ); sessionFactory.getMappingMetamodel() - .forEachEntityDescriptor( entityPersister -> traversableResolver.addPersister( entityPersister, sessionFactory ) ); + .forEachEntityDescriptor( entityPersister -> + traversableResolver.addPersister( entityPersister, sessionFactory ) ); } public boolean onPreInsert(PreInsertEvent event) { @@ -117,58 +121,64 @@ public void onPreUpdateCollection(PreCollectionUpdateEvent event) { ); } - private EntityPersister getEntityPersister(SharedSessionContractImplementor session, String entityName, Object entity) { + private EntityPersister getEntityPersister( + SharedSessionContractImplementor session, String entityName, Object entity) { if ( session != null ) { return session.getEntityPersister( entityName, entity ); } - return entityName == null - ? sessionFactory.getMappingMetamodel().getEntityDescriptor( entity.getClass().getName() ) - : sessionFactory.getMappingMetamodel().getEntityDescriptor( entityName ) - .getSubclassEntityPersister( entity, sessionFactory ); + else { + final var metamodel = sessionFactory.getMappingMetamodel(); + return entityName == null + ? metamodel.getEntityDescriptor( entity.getClass().getName() ) + : metamodel.getEntityDescriptor( entityName ) + .getSubclassEntityPersister( entity, sessionFactory ); + } } - private void validate( - T object, - EntityPersister persister, - GroupsPerOperation.Operation operation) { + private void validate(T object, EntityPersister persister, GroupsPerOperation.Operation operation) { if ( object != null && persister.getRepresentationStrategy().getMode() == RepresentationMode.POJO ) { - final Class[] groups = groupsPerOperation.get( operation ); + final var groups = groupsPerOperation.get( operation ); if ( groups.length > 0 ) { final var constraintViolations = validator.validate( object, groups ); if ( !constraintViolations.isEmpty() ) { - final Set> propagatedViolations = setOfSize( constraintViolations.size() ); + final Set> propagatedViolations = + setOfSize( constraintViolations.size() ); final Set classNames = new HashSet<>(); for ( var violation : constraintViolations ) { BEAN_VALIDATION_LOGGER.trace( violation ); propagatedViolations.add( violation ); classNames.add( violation.getLeafBean().getClass().getName() ); } - final var builder = - new StringBuilder() - .append( "Validation failed for classes " ) - .append( classNames ) - .append( " during " ) - .append( operation.getName() ) - .append( " time for groups " ) - .append( toString( groups ) ) - .append( "\nList of constraint violations:[\n" ); - for ( var violation : constraintViolations ) { - builder.append( "\t" ).append( violation.toString() ).append( "\n" ); - } - builder.append( "]" ); - throw new ConstraintViolationException( builder.toString(), propagatedViolations ); + throw new ConstraintViolationException( + message( operation, classNames, groups, constraintViolations ), + propagatedViolations ); } } } } - private String toString(Class[] groups) { - final var string = new StringBuilder( "[" ); + private String message( + GroupsPerOperation.Operation operation, + Set classNames, + Class[] groups, + Set> constraintViolations) { + final var builder = new StringBuilder(); + builder.append( "Validation failed for classes " ) + .append( classNames ) + .append( " during " ) + .append( operation.getName() ) + .append( " time for groups [" ); for ( var group : groups ) { - string.append( group.getName() ).append( ", " ); + builder.append( group.getName() ).append( ", " ); + } + builder.append( "]\nList of constraint violations:[\n" ); + for ( var violation : constraintViolations ) { + builder.append( "\t" ) + .append( violation.toString() ) + .append( "\n" ); } - string.append( "]" ); - return string.toString(); + builder.append( "]" ); + return builder.toString(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/TypeSafeActivator.java b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/TypeSafeActivator.java index 674181d97bee..a646171ed9d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/TypeSafeActivator.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/TypeSafeActivator.java @@ -106,7 +106,6 @@ else if ( validationModes.contains( ValidationMode.DDL ) ) { // There is a Jakarta Validation provider, but it failed to bootstrap the factory for some reason, // we should fail and let the user deal with it: throw exception; - } } } @@ -148,10 +147,10 @@ private static void setupListener( ValidatorFactory validatorFactory, SessionFactoryServiceRegistry serviceRegistry, SessionFactoryImplementor sessionFactory) { - final var classLoaderService = serviceRegistry.requireService( ClassLoaderService.class ); - final var cfgService = serviceRegistry.requireService( ConfigurationService.class ); final var listener = - new BeanValidationEventListener( validatorFactory, cfgService.getSettings(), classLoaderService ); + new BeanValidationEventListener( validatorFactory, + serviceRegistry.requireService( ConfigurationService.class ).getSettings(), + serviceRegistry.requireService( ClassLoaderService.class ) ); final var listenerRegistry = sessionFactory.getEventListenerRegistry(); listenerRegistry.addDuplicationStrategy( DuplicationStrategyImpl.INSTANCE ); listenerRegistry.appendListeners( EventType.PRE_INSERT, listener ); @@ -183,7 +182,8 @@ private static void applyRelationalConstraints(ValidatorFactory factory, Activat context.getMetadata().getEntityBindings(), serviceRegistry.requireService( ConfigurationService.class ).getSettings(), serviceRegistry.requireService( JdbcServices.class ).getDialect(), - new ClassLoaderAccessImpl( null, serviceRegistry.getService( ClassLoaderService.class ) ) + new ClassLoaderAccessImpl( null, + serviceRegistry.getService( ClassLoaderService.class ) ) ); } } From 9cda810511a1f309204ba69d00eefe82d764eb43 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Thu, 4 Dec 2025 22:10:10 +0100 Subject: [PATCH 018/181] HHH-19975 validate LoadEvent when it is reused --- .../org/hibernate/event/spi/LoadEvent.java | 36 +++++++++++-------- .../org/hibernate/internal/SessionImpl.java | 1 + 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/LoadEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/LoadEvent.java index e85ba703c64a..70b209b7d413 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/LoadEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/LoadEvent.java @@ -4,6 +4,7 @@ */ package org.hibernate.event.spi; +import org.hibernate.Internal; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.Locking; @@ -66,32 +67,36 @@ private LoadEvent( boolean isAssociationFetch, EventSource source, Boolean readOnly) { - super( source ); + this.entityId = entityId; + this.entityClassName = entityClassName; + this.instanceToLoad = instanceToLoad; + this.lockOptions = lockOptions; + this.isAssociationFetch = isAssociationFetch; + this.readOnly = readOnly; + validate(); + } + @Internal + public void validate() { if ( entityId == null ) { - throw new IllegalArgumentException( "id to load is required for loading" ); + throw new IllegalArgumentException( "Identifier may not be null" ); } - if ( lockOptions.getLockMode() == LockMode.WRITE ) { - throw new IllegalArgumentException("Invalid lock mode for loading"); + final var lockMode = lockOptions.getLockMode(); + if ( lockMode == LockMode.WRITE ) { + throw new IllegalArgumentException( "Invalid lock mode: " + LockMode.WRITE ); } - else if ( lockOptions.getLockMode() == null ) { + else if ( lockMode == null ) { lockOptions.setLockMode( LockMode.NONE ); } - - this.entityId = entityId; - this.entityClassName = entityClassName; - this.instanceToLoad = instanceToLoad; - this.lockOptions = lockOptions; - this.isAssociationFetch = isAssociationFetch; - this.readOnly = readOnly; } public Object getEntityId() { return entityId; } + @Internal public void setEntityId(Object entityId) { this.entityId = entityId; } @@ -100,6 +105,7 @@ public String getEntityClassName() { return entityClassName; } + @Internal public void setEntityClassName(String entityClassName) { this.entityClassName = entityClassName; } @@ -108,6 +114,7 @@ public boolean isAssociationFetch() { return isAssociationFetch; } + @Internal public void setAssociationFetch(boolean associationFetch) { isAssociationFetch = associationFetch; } @@ -116,6 +123,7 @@ public Object getInstanceToLoad() { return instanceToLoad; } + @Internal public void setInstanceToLoad(Object instanceToLoad) { this.instanceToLoad = instanceToLoad; } @@ -124,6 +132,7 @@ public LockOptions getLockOptions() { return lockOptions; } + @Internal public void setLockOptions(LockOptions lockOptions) { this.lockOptions = lockOptions; } @@ -140,12 +149,11 @@ public Boolean getReadOnly() { return readOnly; } + @Internal public void setReadOnly(Boolean readOnly) { this.readOnly = readOnly; } - - /** * @deprecated Use {@linkplain #getLockOptions()} instead. */ diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index a37ec581e42c..232f193263c5 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -1128,6 +1128,7 @@ protected LoadEvent makeLoadEvent(String entityName, Object id, Boolean readOnly event.setReadOnly( readOnly ); event.setLockOptions( lockOptions ); event.setAssociationFetch( false ); + event.validate(); return event; } } From c6820d223c9f561c13c967d84064189e03620990 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Thu, 4 Dec 2025 22:42:25 +0100 Subject: [PATCH 019/181] improve some locking-related error messages --- ...tractPessimisticUpdateLockingStrategy.java | 22 +++++++++---------- ...timisticForceIncrementLockingStrategy.java | 10 +++++---- .../lock/OptimisticLockingStrategy.java | 10 +++++---- ...simisticForceIncrementLockingStrategy.java | 10 +++++---- .../PessimisticReadUpdateLockingStrategy.java | 4 ---- ...PessimisticWriteUpdateLockingStrategy.java | 4 ---- .../dialect/lock/UpdateLockingStrategy.java | 2 +- .../DefaultPostLoadEventListener.java | 10 +++++---- .../hibernate/internal/CoreMessageLogger.java | 4 ---- 9 files changed, 35 insertions(+), 41 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/lock/AbstractPessimisticUpdateLockingStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/lock/AbstractPessimisticUpdateLockingStrategy.java index de3ebdbda91e..af1b1c4709d8 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/lock/AbstractPessimisticUpdateLockingStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/lock/AbstractPessimisticUpdateLockingStrategy.java @@ -10,12 +10,11 @@ import org.hibernate.StaleObjectStateException; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.pretty.MessageHelper; import org.hibernate.sql.Update; import java.sql.SQLException; -import static org.hibernate.internal.CoreMessageLogger.CORE_LOGGER; +import static org.hibernate.pretty.MessageHelper.infoString; /** * Common implementation of {@link PessimisticReadUpdateLockingStrategy} @@ -39,13 +38,15 @@ public abstract class AbstractPessimisticUpdateLockingStrategy implements Lockin public AbstractPessimisticUpdateLockingStrategy(EntityPersister lockable, LockMode lockMode) { this.lockable = lockable; this.lockMode = lockMode; - if ( !lockable.isVersioned() ) { - CORE_LOGGER.writeLocksNotSupported( lockable.getEntityName() ); - this.sql = null; + if ( lockMode.lessThan( LockMode.PESSIMISTIC_READ ) ) { + throw new HibernateException( "Lock mode " + lockMode + + " not valid for locking via 'update' statement" ); } - else { - this.sql = generateLockString(); + if ( !lockable.isVersioned() ) { + throw new HibernateException( "Entity '" + lockable.getEntityName() + + "' has no version and may not be locked via 'update' statement" ); } + this.sql = generateLockString(); } @Override @@ -54,14 +55,11 @@ public void lock(Object id, Object version, Object object, int timeout, SharedSe doLock( id, version, session ); } catch (JDBCException e) { - throw new PessimisticEntityLockException( object, "could not obtain pessimistic lock", e ); + throw new PessimisticEntityLockException( object, "Could not obtain pessimistic lock", e ); } } void doLock(Object id, Object version, SharedSessionContractImplementor session) { - if ( !lockable.isVersioned() ) { - throw new HibernateException( "write locks via update not supported for non-versioned entities [" + lockable.getEntityName() + "]" ); - } try { final var factory = session.getFactory(); final var jdbcCoordinator = session.getJdbcCoordinator(); @@ -100,7 +98,7 @@ void doLock(Object id, Object version, SharedSessionContractImplementor session) catch ( SQLException e ) { throw session.getJdbcServices().getSqlExceptionHelper().convert( e, - "could not lock: " + MessageHelper.infoString( lockable, id, session.getFactory() ), + "could not lock: " + infoString( lockable, id, session.getFactory() ), sql ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticForceIncrementLockingStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticForceIncrementLockingStrategy.java index 5cf5ab687068..5276bd42a9d6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticForceIncrementLockingStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticForceIncrementLockingStrategy.java @@ -34,15 +34,17 @@ public OptimisticForceIncrementLockingStrategy(EntityPersister lockable, LockMod this.lockable = lockable; this.lockMode = lockMode; if ( lockMode.lessThan( LockMode.OPTIMISTIC_FORCE_INCREMENT ) ) { - throw new HibernateException( "[" + lockMode + "] not valid for [" + lockable.getEntityName() + "]" ); + throw new HibernateException( "Entity '" + lockable.getEntityName() + + "' may not be locked at level " + lockMode ); + } + if ( !lockable.isVersioned() ) { + throw new HibernateException( "Entity '" + lockable.getEntityName() + + "' has no version and may not be locked at level " + lockMode); } } @Override public void lock(Object id, Object version, Object object, int timeout, EventSource session) { - if ( !lockable.isVersioned() ) { - throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" ); - } // final EntityEntry entry = session.getPersistenceContextInternal().getEntry( object ); // Register the EntityIncrementVersionProcess action to run just prior to transaction commit. session.getActionQueue().registerCallback( new EntityIncrementVersionProcess( object ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticLockingStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticLockingStrategy.java index 79337885d354..8c8c28260760 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticLockingStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticLockingStrategy.java @@ -33,15 +33,17 @@ public OptimisticLockingStrategy(EntityPersister lockable, LockMode lockMode) { this.lockable = lockable; this.lockMode = lockMode; if ( lockMode.lessThan( LockMode.OPTIMISTIC ) ) { - throw new HibernateException( "[" + lockMode + "] not valid for [" + lockable.getEntityName() + "]" ); + throw new HibernateException( "Entity '" + lockable.getEntityName() + + "' may not be locked at level " + lockMode ); + } + if ( !lockable.isVersioned() ) { + throw new HibernateException( "Entity '" + lockable.getEntityName() + + "' has no version and may not be locked at level " + lockMode); } } @Override public void lock(Object id, Object version, Object object, int timeout, EventSource session) { - if ( !lockable.isVersioned() ) { - throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" ); - } // Register the EntityVerifyVersionProcess action to run just prior to transaction commit. session.getActionQueue().registerCallback( new EntityVerifyVersionProcess( object ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticForceIncrementLockingStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticForceIncrementLockingStrategy.java index cc2df7827e9d..b5e1345455cc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticForceIncrementLockingStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticForceIncrementLockingStrategy.java @@ -34,15 +34,17 @@ public PessimisticForceIncrementLockingStrategy(EntityPersister lockable, LockMo this.lockMode = lockMode; // ForceIncrement can be used for PESSIMISTIC_READ, PESSIMISTIC_WRITE or PESSIMISTIC_FORCE_INCREMENT if ( lockMode.lessThan( LockMode.PESSIMISTIC_READ ) ) { - throw new HibernateException( "[" + lockMode + "] not valid for [" + lockable.getEntityName() + "]" ); + throw new HibernateException( "Entity '" + lockable.getEntityName() + + "' may not be locked at level " + lockMode ); + } + if ( !lockable.isVersioned() ) { + throw new HibernateException( "Entity '" + lockable.getEntityName() + + "' has no version and may not be locked at level " + lockMode); } } @Override public void lock(Object id, Object version, Object object, int timeout, SharedSessionContractImplementor session) { - if ( !lockable.isVersioned() ) { - throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" ); - } final var entry = session.getPersistenceContextInternal().getEntry( object ); OptimisticLockHelper.forceVersionIncrement( object, entry, session ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticReadUpdateLockingStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticReadUpdateLockingStrategy.java index 717fe17b9b08..51d222654d43 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticReadUpdateLockingStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticReadUpdateLockingStrategy.java @@ -4,7 +4,6 @@ */ package org.hibernate.dialect.lock; -import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.persister.entity.EntityPersister; @@ -30,8 +29,5 @@ public class PessimisticReadUpdateLockingStrategy extends AbstractPessimisticUpd */ public PessimisticReadUpdateLockingStrategy(EntityPersister lockable, LockMode lockMode) { super( lockable, lockMode ); - if ( lockMode.lessThan( LockMode.PESSIMISTIC_READ ) ) { - throw new HibernateException( "[" + lockMode + "] not valid for update statement" ); - } } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticWriteUpdateLockingStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticWriteUpdateLockingStrategy.java index b809344f1d78..0bfab56ad135 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticWriteUpdateLockingStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticWriteUpdateLockingStrategy.java @@ -4,7 +4,6 @@ */ package org.hibernate.dialect.lock; -import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.persister.entity.EntityPersister; @@ -29,8 +28,5 @@ public class PessimisticWriteUpdateLockingStrategy extends AbstractPessimisticUp */ public PessimisticWriteUpdateLockingStrategy(EntityPersister lockable, LockMode lockMode) { super( lockable, lockMode ); - if ( lockMode.lessThan( LockMode.PESSIMISTIC_READ ) ) { - throw new HibernateException( "[" + lockMode + "] not valid for update statement" ); - } } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/lock/UpdateLockingStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/lock/UpdateLockingStrategy.java index cc0e93d1ba20..1e7235629d82 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/lock/UpdateLockingStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/lock/UpdateLockingStrategy.java @@ -35,7 +35,7 @@ public class UpdateLockingStrategy extends AbstractPessimisticUpdateLockingStrat public UpdateLockingStrategy(EntityPersister lockable, LockMode lockMode) { super( lockable, lockMode ); if ( lockMode.lessThan( LockMode.WRITE ) ) { - throw new HibernateException( "[" + lockMode + "] not valid for update statement" ); + throw new HibernateException( "Lock mode " + lockMode + " not valid for locking via 'update' statement" ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPostLoadEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPostLoadEventListener.java index a929a2362b94..0b6516e45cc1 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPostLoadEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPostLoadEventListener.java @@ -51,16 +51,18 @@ public void onPostLoad(PostLoadEvent event) { OptimisticLockHelper.forceVersionIncrement( entity, entry, session ); break; case OPTIMISTIC_FORCE_INCREMENT: - session.getActionQueue().registerCallback( new EntityIncrementVersionProcess( entity ) ); + session.getActionQueue() + .registerCallback( new EntityIncrementVersionProcess( entity ) ); break; case OPTIMISTIC: - session.getActionQueue().registerCallback( new EntityVerifyVersionProcess( entity ) ); + session.getActionQueue() + .registerCallback( new EntityVerifyVersionProcess( entity ) ); break; } } else { - throw new HibernateException("[" + lockMode - + "] not supported for non-versioned entities [" + persister.getEntityName() + "]"); + throw new HibernateException( "Entity '" + persister.getEntityName() + + "' has no version and may not be locked at level " + lockMode); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java index 1213a3bb9bb0..ab6bba599e04 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java @@ -270,10 +270,6 @@ void missingArguments( @Message(value = "Warnings creating temp table: %s", id = 413) void warningsCreatingTempTable(SQLWarning warning); - @LogMessage(level = WARN) - @Message(value = "Write locks via update not supported for non-versioned entities [%s]", id = 416) - void writeLocksNotSupported(String entityName); - @LogMessage(level = WARN) @Message( value = """ From c3ebcd20d07b75cbb20f8f1f68af2aa12276ccd9 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Thu, 4 Dec 2025 16:22:59 +0100 Subject: [PATCH 020/181] HHH-19977 Relax scopes to allow HR to run a reactive flush A native query might need to run a flush operation before it's executed. Hibernate Reactive have to run a reactive flush. By relaxing the scopes of these methods, we can avoid some code duplication. --- .../query/spi/AbstractSelectionQuery.java | 16 ++++++++++++++-- .../query/sql/internal/NativeQueryImpl.java | 15 +++++++++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java index b6d2f7cc540f..2238e267c70c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java @@ -180,16 +180,28 @@ protected void beforeQuery() { session.prepareForQueryExecution( requiresTxn( options.getLockOptions().getLockMode() ) ); prepareForExecution(); + prepareSessionFlushMode( session ); + prepareSessionCacheMode( session ); + } - assert sessionFlushMode == null; - assert sessionCacheMode == null; + /* + * Used by Hibernate Reactive + */ + protected void prepareSessionFlushMode(SharedSessionContractImplementor session) { + assert sessionFlushMode == null; final var effectiveFlushMode = getQueryOptions().getFlushMode(); if ( effectiveFlushMode != null && session instanceof SessionImplementor statefulSession ) { sessionFlushMode = statefulSession.getHibernateFlushMode(); statefulSession.setHibernateFlushMode( effectiveFlushMode ); } + } + /* + * Used by Hibernate Reactive + */ + protected void prepareSessionCacheMode(SharedSessionContractImplementor session) { + assert sessionCacheMode == null; final var effectiveCacheMode = getCacheMode(); if ( effectiveCacheMode != null ) { sessionCacheMode = session.getCacheMode(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java index b960851b3c3a..806fb177c50a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java @@ -554,6 +554,14 @@ public boolean hasCallbackActions() { return callback != null && callback.hasAfterLoadActions(); } + /* + * Used by Hibernate Reactive + */ + @Override + protected void resetCallback() { + callback = null; + } + @Override public QueryParameterBindings getQueryParameterBindings() { return parameterBindings; @@ -722,14 +730,17 @@ protected void prepareForExecution() { getSession().flush(); } // Reset the callback before every execution - callback = null; + resetCallback(); } // Otherwise, the application specified query spaces via the Hibernate // SynchronizeableQuery and so the query will already perform a partial // flush according to the defined query spaces - no need for a full flush. } - private boolean shouldFlush() { + /* + * Used by Hibernate Reactive + */ + protected boolean shouldFlush() { if ( getSession().isTransactionInProgress() ) { final var flushMode = getQueryOptions().getFlushMode(); return switch ( flushMode == null ? getSession().getHibernateFlushMode() : flushMode ) { From b9182723a2298b397b2dec19ff469391741b0fae Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 5 Dec 2025 10:07:12 +0100 Subject: [PATCH 021/181] refactoring and cleanup to the procedure.internal package --- .../NamedProcedureCallDefinitionImpl.java | 7 +- .../internal/EntityDomainResultBuilder.java | 4 +- .../internal/FunctionReturnImpl.java | 13 +- .../procedure/internal/ProcedureCallImpl.java | 317 ++++++++---------- .../internal/ProcedureOutputsImpl.java | 26 +- .../internal/ProcedureParamBindings.java | 57 ++-- .../ProcedureParameterBindingImpl.java | 4 +- .../internal/ProcedureParameterImpl.java | 75 +++-- .../ProcedureParameterMetadataImpl.java | 16 +- .../internal/ScalarDomainResultBuilder.java | 4 +- .../hibernate/procedure/internal/Util.java | 37 +- ...ProcedureParameterMetadataImplementor.java | 16 + 12 files changed, 275 insertions(+), 301 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/NamedProcedureCallDefinitionImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/NamedProcedureCallDefinitionImpl.java index 8cc3c7e380c6..a0eb40699f28 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/NamedProcedureCallDefinitionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/NamedProcedureCallDefinitionImpl.java @@ -20,7 +20,6 @@ import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.procedure.internal.NamedCallableQueryMementoImpl; -import org.hibernate.procedure.internal.Util; import org.hibernate.procedure.spi.NamedCallableQueryMemento; import org.hibernate.procedure.spi.ParameterStrategy; import org.hibernate.query.results.ResultSetMapping; @@ -29,6 +28,8 @@ import jakarta.persistence.ParameterMode; import jakarta.persistence.StoredProcedureParameter; +import static org.hibernate.procedure.internal.Util.resolveResultSetMappingClasses; +import static org.hibernate.procedure.internal.Util.resolveResultSetMappingNames; import static org.hibernate.procedure.spi.NamedCallableQueryMemento.ParameterMemento; /** @@ -96,7 +97,7 @@ public NamedCallableQueryMemento resolve(SessionFactoryImplementor sessionFactor final ResultSetMapping resultSetMapping = buildResultSetMapping( registeredName, sessionFactory ); if ( specifiesResultClasses ) { - Util.resolveResultSetMappingClasses( + resolveResultSetMappingClasses( resultClasses, resultSetMapping, collectedQuerySpaces::add, @@ -104,7 +105,7 @@ public NamedCallableQueryMemento resolve(SessionFactoryImplementor sessionFactor ); } else if ( specifiesResultSetMappings ) { - Util.resolveResultSetMappingNames( + resolveResultSetMappingNames( resultSetMappings, resultSetMapping, collectedQuerySpaces::add, diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/EntityDomainResultBuilder.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/EntityDomainResultBuilder.java index 11a3f45b0955..3474537561be 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/EntityDomainResultBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/EntityDomainResultBuilder.java @@ -19,13 +19,13 @@ /** * @author Christian Beikov */ -public class EntityDomainResultBuilder implements ResultBuilder { +class EntityDomainResultBuilder implements ResultBuilder { private final NavigablePath navigablePath; private final EntityMappingType entityDescriptor; private final FetchBuilderBasicValued discriminatorFetchBuilder; - public EntityDomainResultBuilder(EntityMappingType entityDescriptor) { + EntityDomainResultBuilder(EntityMappingType entityDescriptor) { this.entityDescriptor = entityDescriptor; this.navigablePath = new NavigablePath( entityDescriptor.getEntityName() ); final EntityDiscriminatorMapping discriminatorMapping = entityDescriptor.getDiscriminatorMapping(); diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/FunctionReturnImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/FunctionReturnImpl.java index fce45e336280..887f9ecdc258 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/FunctionReturnImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/FunctionReturnImpl.java @@ -17,23 +17,20 @@ import org.hibernate.sql.exec.internal.JdbcCallParameterExtractorImpl; import org.hibernate.sql.exec.internal.JdbcCallRefCursorExtractorImpl; import org.hibernate.sql.exec.spi.JdbcCallFunctionReturn; -import org.hibernate.type.BasicType; -import org.hibernate.type.descriptor.java.JavaType; -import org.hibernate.type.spi.TypeConfiguration; import jakarta.persistence.ParameterMode; /** * @author Steve Ebersole */ -public class FunctionReturnImpl implements FunctionReturnImplementor { +class FunctionReturnImpl implements FunctionReturnImplementor { private final ProcedureCallImplementor procedureCall; private final int sqlTypeCode; private OutputableType ormType; - public FunctionReturnImpl(ProcedureCallImplementor procedureCall, int sqlTypeCode) { + FunctionReturnImpl(ProcedureCallImplementor procedureCall, int sqlTypeCode) { this.procedureCall = procedureCall; this.sqlTypeCode = sqlTypeCode; } @@ -67,11 +64,11 @@ private OutputableType getOrmType(SharedSessionContractImplementor persistenc return ormType; } else { - final TypeConfiguration typeConfiguration = persistenceContext.getFactory().getTypeConfiguration(); - final JavaType javaType = + final var typeConfiguration = persistenceContext.getFactory().getTypeConfiguration(); + final var javaType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( getJdbcTypeCode() ) .getJdbcRecommendedJavaTypeMapping( null, null, typeConfiguration ); - final BasicType basicType = + final var basicType = typeConfiguration.standardBasicTypeForJavaType( javaType.getJavaTypeClass() ); //noinspection unchecked return (OutputableType) basicType; diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java index 798d3f729b57..3cc5a614cee4 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java @@ -31,11 +31,13 @@ import org.hibernate.procedure.ProcedureOutputs; import org.hibernate.procedure.spi.FunctionReturnImplementor; import org.hibernate.procedure.spi.NamedCallableQueryMemento; +import org.hibernate.procedure.spi.NamedCallableQueryMemento.ParameterMemento; import org.hibernate.procedure.spi.ParameterStrategy; import org.hibernate.procedure.spi.ProcedureCallImplementor; import org.hibernate.procedure.spi.ProcedureParameterImplementor; import org.hibernate.query.KeyedPage; import org.hibernate.query.KeyedResultList; +import org.hibernate.query.spi.ProcedureParameterMetadataImplementor; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SqmBindableType; import org.hibernate.sql.exec.spi.JdbcOperationQueryCall; @@ -50,8 +52,6 @@ import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.ScrollableResultsImplementor; -import org.hibernate.query.sqm.SqmExpressible; -import org.hibernate.result.Output; import org.hibernate.result.ResultSetOutput; import org.hibernate.result.UpdateCountOutput; import org.hibernate.result.internal.OutputsExecutionContext; @@ -78,6 +78,7 @@ import jakarta.persistence.metamodel.Type; import static java.lang.Boolean.parseBoolean; +import static java.lang.Integer.parseInt; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; @@ -89,6 +90,7 @@ import static org.hibernate.jpa.HibernateHints.HINT_CALLABLE_FUNCTION_RETURN_TYPE; import static org.hibernate.procedure.internal.NamedCallableQueryMementoImpl.ParameterMementoImpl.fromRegistration; import static org.hibernate.procedure.internal.Util.resolveResultSetMappingClasses; +import static org.hibernate.procedure.internal.Util.resolveResultSetMappingNames; import static org.hibernate.procedure.internal.Util.resolveResultSetMappings; import static org.hibernate.query.results.ResultSetMapping.resolveResultSetMapping; @@ -105,7 +107,7 @@ public class ProcedureCallImpl private FunctionReturnImpl functionReturn; - private final ProcedureParameterMetadataImpl parameterMetadata; + private final ProcedureParameterMetadataImplementor parameterMetadata; private final ProcedureParamBindings parameterBindings; private final ResultSetMapping resultSetMapping; @@ -117,29 +119,45 @@ public class ProcedureCallImpl private ProcedureOutputsImpl outputs; private static String mappingId(String procedureName, Class[] resultClasses) { + assert resultClasses != null && resultClasses.length > 0; return procedureName + ":" + join( ",", resultClasses ); } private static String mappingId(String procedureName, String[] resultSetMappingNames) { + assert resultSetMappingNames != null && resultSetMappingNames.length > 0; return procedureName + ":" + join( ",", resultSetMappingNames ); } + private void registerParameters(SharedSessionContractImplementor session, NamedCallableQueryMemento memento) { + memento.getParameterMementos() + .forEach( parameterMemento -> registerParameter( parameterMemento.resolve( session ) ) ); + } + + private ProcedureCallImpl( + SharedSessionContractImplementor session, + String procedureName, + String resultSetMappingName, + boolean resultSetMappingDynamic, + Set synchronizedQuerySpaces) { + super( session ); + this.procedureName = procedureName; + this.synchronizedQuerySpaces = synchronizedQuerySpaces; + final var factory = session.getSessionFactory(); + resultSetMapping = resolveResultSetMapping( resultSetMappingName, resultSetMappingDynamic, factory ); + parameterMetadata = new ProcedureParameterMetadataImpl(); + parameterBindings = new ProcedureParamBindings( parameterMetadata, factory ); + } + /** * The no-returns form. * * @param session The session * @param procedureName The name of the procedure to call */ - public ProcedureCallImpl(SharedSessionContractImplementor session, String procedureName) { - super( session ); - this.procedureName = procedureName; - - parameterMetadata = new ProcedureParameterMetadataImpl(); - parameterBindings = new ProcedureParamBindings( parameterMetadata, getSessionFactory() ); - - resultSetMapping = resolveResultSetMapping( procedureName, true, session.getSessionFactory() ); - - synchronizedQuerySpaces = null; + public ProcedureCallImpl( + SharedSessionContractImplementor session, + String procedureName) { + this( session, procedureName, procedureName, true, null ); } /** @@ -149,22 +167,14 @@ public ProcedureCallImpl(SharedSessionContractImplementor session, String proced * @param procedureName The name of the procedure to call * @param resultClasses The classes making up the result */ - public ProcedureCallImpl(SharedSessionContractImplementor session, String procedureName, Class... resultClasses) { - super( session ); - - assert resultClasses != null && resultClasses.length > 0; - - this.procedureName = procedureName; - - final var factory = session.getSessionFactory(); - - parameterMetadata = new ProcedureParameterMetadataImpl(); - parameterBindings = new ProcedureParamBindings( parameterMetadata, factory ); - - synchronizedQuerySpaces = new HashSet<>(); - - resultSetMapping = resolveResultSetMapping( mappingId( procedureName, resultClasses ), factory ); - + public ProcedureCallImpl( + SharedSessionContractImplementor session, + String procedureName, + Class... resultClasses) { + this( session, procedureName, + mappingId( procedureName, resultClasses ), + false, + new HashSet<>() ); resolveResultSetMappingClasses( resultClasses, resultSetMapping, @@ -184,22 +194,11 @@ public ProcedureCallImpl( final SharedSessionContractImplementor session, String procedureName, String... resultSetMappingNames) { - super( session ); - - assert resultSetMappingNames != null && resultSetMappingNames.length > 0; - - this.procedureName = procedureName; - - final var factory = session.getSessionFactory(); - - parameterMetadata = new ProcedureParameterMetadataImpl(); - parameterBindings = new ProcedureParamBindings( parameterMetadata, factory ); - - synchronizedQuerySpaces = new HashSet<>(); - - resultSetMapping = resolveResultSetMapping( mappingId( procedureName, resultSetMappingNames ), factory ); - - Util.resolveResultSetMappingNames( + this( session, procedureName, + mappingId( procedureName, resultSetMappingNames ), + false, + new HashSet<>() ); + resolveResultSetMappingNames( resultSetMappingNames, resultSetMapping, synchronizedQuerySpaces::add, @@ -213,20 +212,13 @@ public ProcedureCallImpl( * @param session The session * @param memento The named/stored memento */ - ProcedureCallImpl(SharedSessionContractImplementor session, NamedCallableQueryMemento memento) { - super( session ); - - procedureName = memento.getCallableName(); - - final var factory = session.getSessionFactory(); - - parameterMetadata = new ProcedureParameterMetadataImpl( memento, session ); - parameterBindings = new ProcedureParamBindings( parameterMetadata, factory ); - - synchronizedQuerySpaces = makeCopy( memento.getQuerySpaces() ); - - resultSetMapping = resolveResultSetMapping( memento.getRegistrationName(), factory ); - + ProcedureCallImpl( + SharedSessionContractImplementor session, + NamedCallableQueryMemento memento) { + this( session, memento.getCallableName(), + memento.getRegistrationName(), + false, + makeCopy( memento.getQuerySpaces() ) ); resolveResultSetMappings( memento.getResultSetMappingNames(), memento.getResultSetMappingClasses(), @@ -234,7 +226,7 @@ public ProcedureCallImpl( synchronizedQuerySpaces::add, this::getSessionFactory ); - + registerParameters( session, memento ); applyOptions( memento ); } @@ -248,19 +240,10 @@ public ProcedureCallImpl( SharedSessionContractImplementor session, NamedCallableQueryMemento memento, Class... resultTypes) { - super( session ); - - procedureName = memento.getCallableName(); - - final var factory = session.getSessionFactory(); - - parameterMetadata = new ProcedureParameterMetadataImpl( memento, session ); - parameterBindings = new ProcedureParamBindings( parameterMetadata, factory ); - - synchronizedQuerySpaces = makeCopy( memento.getQuerySpaces() ); - - resultSetMapping = resolveResultSetMapping( mappingId( procedureName, resultTypes ), factory ); - + this( session, memento.getCallableName(), + mappingId( memento.getCallableName(), resultTypes ), + false, + makeCopy( memento.getQuerySpaces() ) ); resolveResultSetMappings( null, resultTypes, @@ -268,7 +251,7 @@ public ProcedureCallImpl( synchronizedQuerySpaces::add, this::getSessionFactory ); - + registerParameters( session, memento ); applyOptions( memento ); } @@ -276,19 +259,10 @@ public ProcedureCallImpl( SharedSessionContractImplementor session, NamedCallableQueryMementoImpl memento, String... resultSetMappingNames) { - super( session ); - - procedureName = memento.getCallableName(); - - final var factory = session.getSessionFactory(); - - parameterMetadata = new ProcedureParameterMetadataImpl( memento, session ); - parameterBindings = new ProcedureParamBindings( parameterMetadata, factory ); - - synchronizedQuerySpaces = makeCopy( memento.getQuerySpaces() ); - - resultSetMapping = resolveResultSetMapping( mappingId( procedureName, resultSetMappingNames ), factory ); - + this( session, memento.getCallableName(), + mappingId( memento.getCallableName(), resultSetMappingNames ), + false, + makeCopy( memento.getQuerySpaces() ) ); resolveResultSetMappings( resultSetMappingNames, null, @@ -296,7 +270,7 @@ public ProcedureCallImpl( synchronizedQuerySpaces::add, this::getSessionFactory ); - + registerParameters( session, memento ); applyOptions( memento ); } @@ -332,7 +306,7 @@ public MutableQueryOptions getQueryOptions() { } @Override - public ProcedureParameterMetadataImpl getParameterMetadata() { + public ProcedureParameterMetadataImplementor getParameterMetadata() { return parameterMetadata; } @@ -369,7 +343,7 @@ private void markAsFunctionCallRefRefCursor() { public ProcedureCallImpl markAsFunctionCall(Class resultType) { final var basicType = getTypeConfiguration().getBasicTypeForJavaType( resultType ); if ( basicType == null ) { - throw new IllegalArgumentException( "Could not resolve a BasicType for the java type: " + resultType.getName() ); + throw new IllegalArgumentException( "Could not resolve a BasicType for the Java type: " + resultType.getName() ); } markAsFunctionCall( basicType ); return this; @@ -381,7 +355,7 @@ public ProcedureCall markAsFunctionCall(Type typeReference) { throw new IllegalArgumentException( "Given type is not an OutputableType: " + typeReference ); } if ( resultSetMapping.getNumberOfResultBuilders() == 0 ) { - final SqmExpressible expressible = resolveExpressible( typeReference ); + final var expressible = resolveExpressible( typeReference ); // Function returns might not be represented as callable parameters, // but we still want to convert the result to the requested java type if possible resultSetMapping.addResultBuilder( new ScalarDomainResultBuilder<>( expressible.getExpressibleJavaType() ) ); @@ -510,12 +484,12 @@ private SqmBindableType resolveExpressible(Type typeReference) { } private void registerParameter(ProcedureParameterImplementor parameter) { - getParameterMetadata().registerParameter( parameter ); + parameterMetadata.registerParameter( parameter ); } @Override public ProcedureParameterImplementor getParameterRegistration(int position) { - return getParameterMetadata().getQueryParameter( position ); + return parameterMetadata.getQueryParameter( position ); } @Override @@ -556,12 +530,12 @@ public ProcedureParameterImplementor registerParameter( @Override public ProcedureParameterImplementor getParameterRegistration(String name) { - return getParameterMetadata().getQueryParameter( name ); + return parameterMetadata.getQueryParameter( name ); } @Override public List> getRegisteredParameters() { - return unmodifiableList( getParameterMetadata().getRegistrationsAsList() ); + return unmodifiableList( parameterMetadata.getRegistrationsAsList() ); } @Override @@ -651,8 +625,9 @@ private ProcedureOutputsImpl buildOutputs() { private Map, JdbcCallParameterRegistration> collectParameterRegistrations(JdbcOperationQueryCall call) { final Map, JdbcCallParameterRegistration> parameterRegistrations = new IdentityHashMap<>(); - if ( call.getFunctionReturn() != null ) { - parameterRegistrations.put( functionReturn, call.getFunctionReturn() ); + final var funReturn = call.getFunctionReturn(); + if ( funReturn != null ) { + parameterRegistrations.put( functionReturn, funReturn ); } final var registrations = getParameterMetadata().getRegistrationsAsList(); final var jdbcParameters = call.getParameterRegistrations(); @@ -665,8 +640,9 @@ private Map, JdbcCallParameterRegistration> collectParamet private List collectRefCursorExtractors(JdbcOperationQueryCall call) { final List refCursorExtractors = new ArrayList<>(); - if ( call.getFunctionReturn() != null ) { - final var refCursorExtractor = call.getFunctionReturn().getRefCursorExtractor(); + final var funReturn = call.getFunctionReturn(); + if ( funReturn != null ) { + final var refCursorExtractor = funReturn.getRefCursorExtractor(); if ( refCursorExtractor != null ) { refCursorExtractors.add( refCursorExtractor ); } @@ -674,8 +650,7 @@ private List collectRefCursorExtractors(JdbcOperatio final var registrations = getParameterMetadata().getRegistrationsAsList(); final var jdbcParameters = call.getParameterRegistrations(); for ( int i = 0; i < registrations.size(); i++ ) { - final var jdbcCallParameterRegistration = jdbcParameters.get( i ); - final var refCursorExtractor = jdbcCallParameterRegistration.getRefCursorExtractor(); + final var refCursorExtractor = jdbcParameters.get( i ).getRefCursorExtractor(); if ( refCursorExtractor != null ) { refCursorExtractors.add( refCursorExtractor ); } @@ -685,7 +660,7 @@ private List collectRefCursorExtractors(JdbcOperatio private JdbcParameterBindings parameterBindings( Map, JdbcCallParameterRegistration> parameterRegistrations) { - final JdbcParameterBindings jdbcParameterBindings = + final var jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterRegistrations.size() ); for ( var entry : parameterRegistrations.entrySet() ) { final var registration = entry.getValue(); @@ -694,20 +669,18 @@ private JdbcParameterBindings parameterBindings( final var parameter = entry.getKey(); final var binding = getParameterBindings().getBinding( parameter ); if ( !binding.isBound() ) { - if ( parameter.getPosition() == null ) { - throw new IllegalArgumentException( "The parameter named [" + parameter + "] was not set! You need to call the setParameter method." ); + if ( parameter.isNamed() ) { + throw new IllegalArgumentException( "The parameter named '" + parameter + "' was not set" ); } else { - throw new IllegalArgumentException( "The parameter at position [" + parameter + "] was not set! You need to call the setParameter method." ); + throw new IllegalArgumentException( "The parameter at position " + parameter + " was not set" ); } } final var parameterType = (JdbcMapping) registration.getParameterType(); jdbcParameterBindings.addBinding( (JdbcParameter) parameterBinder, - new JdbcParameterBindingImpl( - parameterType, - parameterType.convertToRelationalValue( binding.getBindValue() ) - ) + new JdbcParameterBindingImpl( parameterType, + parameterType.convertToRelationalValue( binding.getBindValue() ) ) ); } } @@ -785,14 +758,13 @@ public NamedCallableQueryMemento toMemento(String name) { ); } - private static List toParameterMementos( - ProcedureParameterMetadataImpl parameterMetadata) { + private static List toParameterMementos( + ProcedureParameterMetadataImplementor parameterMetadata) { if ( parameterMetadata.getParameterStrategy() == ParameterStrategy.UNKNOWN ) { - // none... return emptyList(); } else { - final List mementos = new ArrayList<>(); + final List mementos = new ArrayList<>(); parameterMetadata.visitRegistrations( queryParameter -> mementos.add( fromRegistration( (ProcedureParameterImplementor) queryParameter ) ) @@ -815,12 +787,10 @@ public Query applyGraph(@SuppressWarnings("rawtypes") RootGraph graph, GraphS // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // outputs - private ProcedureOutputs procedureResult; - @Override public boolean execute() { try { - return outputs().getCurrent() instanceof ResultSetOutput; + return getOutputs().getCurrent() instanceof ResultSetOutput; } catch (NoMoreOutputsException e) { return false; @@ -834,30 +804,23 @@ public boolean execute() { } } - protected ProcedureOutputs outputs() { - if ( procedureResult == null ) { - procedureResult = getOutputs(); - } - return procedureResult; - } - @Override protected int doExecuteUpdate() { - // the expectation is that there is just one Output, of type UpdateCountOutput + // The expectation is that there is just one Output, of type UpdateCountOutput try { execute(); return getUpdateCount(); } finally { - outputs().release(); + getOutputs().release(); } } @Override public Object getOutputParameterValue(int position) { - // NOTE : according to spec (specifically), an exception thrown from this method should not mark for rollback. + // According to spec, an exception thrown from this method should not mark for rollback. try { - return outputs().getOutputParameterValue( position ); + return getOutputs().getOutputParameterValue( position ); } catch (ParameterStrategyException e) { throw new IllegalArgumentException( "Invalid mix of named and positional parameters", e ); @@ -869,9 +832,9 @@ public Object getOutputParameterValue(int position) { @Override public Object getOutputParameterValue(String parameterName) { - // NOTE : according to spec (specifically), an exception thrown from this method should not mark for rollback. + // According to spec, an exception thrown from this method should not mark for rollback. try { - return outputs().getOutputParameterValue( parameterName ); + return getOutputs().getOutputParameterValue( parameterName ); } catch (ParameterStrategyException e) { throw new IllegalArgumentException( "Invalid mix of named and positional parameters", e ); @@ -883,17 +846,19 @@ public Object getOutputParameterValue(String parameterName) { @Override public boolean hasMoreResults() { - return outputs().goToNext() && outputs().getCurrent() instanceof ResultSetOutput; + final var outputs = getOutputs(); + return outputs.goToNext() + && outputs.getCurrent() instanceof ResultSetOutput; } @Override public int getUpdateCount() { try { - final Output rtn = outputs().getCurrent(); - if ( rtn == null ) { + final var output = getOutputs().getCurrent(); + if ( output == null ) { return -1; } - else if ( rtn instanceof UpdateCountOutput updateCount ) { + else if ( output instanceof UpdateCountOutput updateCount ) { return updateCount.getUpdateCount(); } else { @@ -919,7 +884,7 @@ protected List doList() { } else { try { - if ( outputs().getCurrent() instanceof ResultSetOutput resultSetOutput ) { + if ( getOutputs().getCurrent() instanceof ResultSetOutput resultSetOutput ) { return ((ResultSetOutput) resultSetOutput).getResultList(); } else { @@ -928,9 +893,9 @@ protected List doList() { } } catch (NoMoreOutputsException e) { - // todo : the spec is completely silent on these type of edge-case scenarios. - // Essentially here we'd have a case where there are no more results (ResultSets nor updateCount) but - // getResultList was called. + // TODO: the spec is completely silent on these type of edge-case scenarios. + // Essentially here we'd have a case where there are no more results + // (ResultSets nor updateCount) but getResultList() was called. return null; } catch (HibernateException he) { @@ -972,22 +937,13 @@ public List getResultList() { public R getSingleResult() { final var resultList = getResultList(); if ( resultList == null || resultList.isEmpty() ) { - throw new NoResultException( - String.format( - "Call to stored procedure [%s] returned no results", - getProcedureName() - ) - ); + throw new NoResultException( "Call to stored procedure '" + getProcedureName() + + "' returned no results" ); } else if ( resultList.size() > 1 ) { - throw new NonUniqueResultException( - String.format( - "Call to stored procedure [%s] returned multiple results", - getProcedureName() - ) - ); + throw new NonUniqueResultException( "Call to stored procedure '" + getProcedureName() + + "' returned multiple results" ); } - return resultList.get( 0 ); } @@ -998,14 +954,9 @@ public R getSingleResultOrNull() { return null; } else if ( resultList.size() > 1 ) { - throw new NonUniqueResultException( - String.format( - "Call to stored procedure [%s] returned multiple results", - getProcedureName() - ) - ); + throw new NonUniqueResultException( "Call to stored procedure '" + getProcedureName() + + "' returned multiple results" ); } - return resultList.get( 0 ); } @@ -1046,7 +997,7 @@ public T unwrap(Class type) { public ProcedureCallImplementor setLockMode(LockModeType lockMode) { // the JPA spec requires IllegalStateException here, even // though it's logically an UnsupportedOperationException - throw new IllegalStateException( "Illegal attempt to set lock mode for a procedure calls" ); + throw new IllegalStateException( "Illegal attempt to set lock mode for a procedure call" ); } @Override @@ -1062,7 +1013,7 @@ public ProcedureCallImplementor setTimeout(Integer timeout) { public LockModeType getLockMode() { // the JPA spec requires IllegalStateException here, even // though it's logically an UnsupportedOperationException - throw new IllegalStateException( "Illegal attempt to get lock mode on a native-query" ); + throw new IllegalStateException( "Illegal attempt to get lock mode for a procedure call" ); } @Override @Deprecated @@ -1070,39 +1021,33 @@ public QueryImplementor setLockOptions(LockOptions lockOptions) { throw new UnsupportedOperationException( "setLockOptions does not apply to procedure calls" ); } - @Override + @Override @SuppressWarnings("resource") public ProcedureCallImplementor setHint(String hintName, Object value) { switch ( hintName ) { case HINT_CALLABLE_FUNCTION: - if ( value != null ) { - if ( value instanceof Boolean bool ) { - if ( bool ) { - applyCallableFunctionHint(); - } - } - else if ( parseBoolean( value.toString() ) ) { - applyCallableFunctionHint(); - } + if ( value instanceof Boolean bool && bool + || value instanceof String string && parseBoolean( string ) ) { + applyCallableFunctionHint(); + } + else { + throw new IllegalArgumentException( "Illegal value for hint '" + hintName + "'" ); } break; case HINT_CALLABLE_FUNCTION_RETURN_TYPE: - if ( value != null ) { - if ( value instanceof Integer code ) { - //noinspection resource - markAsFunctionCall( code ); - } - else if ( value instanceof Type type ) { - //noinspection resource - markAsFunctionCall( type ); - } - else if ( value instanceof Class type ) { - //noinspection resource - markAsFunctionCall( type ); - } - else { - //noinspection resource - markAsFunctionCall( Integer.parseInt( value.toString() ) ); - } + if ( value instanceof Integer code ) { + markAsFunctionCall( code ); + } + else if ( value instanceof Type type ) { + markAsFunctionCall( type ); + } + else if ( value instanceof Class type ) { + markAsFunctionCall( type ); + } + else if ( value instanceof String string ) { + markAsFunctionCall( parseInt( string ) ); + } + else { + throw new IllegalArgumentException( "Illegal value for hint '" + hintName + "'" ); } break; default: diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java index d4686e03774f..5e7942736e49 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java @@ -5,7 +5,6 @@ package org.hibernate.procedure.internal; import java.sql.CallableStatement; -import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -26,7 +25,7 @@ * * @author Steve Ebersole */ -public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutputs { +class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutputs { private final ProcedureCallImpl procedureCall; private final CallableStatement callableStatement; @@ -53,7 +52,7 @@ public T getOutputParameterValue(ProcedureParameter parameter) { if ( parameter.getMode() == ParameterMode.IN ) { throw new ParameterMisuseException( "IN parameter not valid for output extraction" ); } - final JdbcCallParameterRegistration registration = parameterRegistrations.get( parameter ); + final var registration = parameterRegistrations.get( parameter ); if ( registration == null ) { throw new IllegalArgumentException( "Parameter [" + parameter + "] is not registered with this procedure call" ); } @@ -108,7 +107,7 @@ private ProcedureCurrentReturnState(boolean isResultSet, int updateCount, int re @Override public boolean indicatesMoreOutputs() { return super.indicatesMoreOutputs() - || ProcedureOutputsImpl.this.refCursorParamIndex < refCursorParameters.length; + || ProcedureOutputsImpl.this.refCursorParamIndex < refCursorParameters.length; } @Override @@ -118,8 +117,8 @@ protected boolean hasExtendedReturns() { @Override protected Output buildExtendedReturn() { - final JdbcCallRefCursorExtractor refCursorParam = refCursorParameters[ProcedureOutputsImpl.this.refCursorParamIndex++]; - final ResultSet resultSet = refCursorParam.extractResultSet( + final var refCursorParam = refCursorParameters[ProcedureOutputsImpl.this.refCursorParamIndex++]; + final var resultSet = refCursorParam.extractResultSet( callableStatement, procedureCall.getSession() ); @@ -133,13 +132,14 @@ protected boolean hasFunctionReturns() { @Override protected Output buildFunctionReturn() { - final Object result = parameterRegistrations.get( procedureCall.getFunctionReturn() ) - .getParameterExtractor() - .extractValue( - callableStatement, - false, - procedureCall.getSession() - ); + final Object result = + parameterRegistrations.get( procedureCall.getFunctionReturn() ) + .getParameterExtractor() + .extractValue( + callableStatement, + false, + procedureCall.getSession() + ); final List results = new ArrayList<>( 1 ); results.add( result ); return buildResultSetOutput( () -> results ); diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParamBindings.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParamBindings.java index f53fb220006a..95ed5c205ac4 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParamBindings.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParamBindings.java @@ -14,6 +14,7 @@ import org.hibernate.procedure.spi.ProcedureParameterBinding; import org.hibernate.procedure.spi.ProcedureParameterImplementor; import org.hibernate.query.QueryParameter; +import org.hibernate.query.spi.ProcedureParameterMetadataImplementor; import org.hibernate.query.spi.QueryParameterBinding; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterImplementor; @@ -29,87 +30,89 @@ public class ProcedureParamBindings implements QueryParameterBindings { private static final Logger LOG = Logger.getLogger( QueryParameterBindings.class ); - private final ProcedureParameterMetadataImpl parameterMetadata; + private final ProcedureParameterMetadataImplementor parameterMetadata; private final SessionFactoryImplementor sessionFactory; private final Map, ProcedureParameterBinding> bindingMap = new HashMap<>(); public ProcedureParamBindings( - ProcedureParameterMetadataImpl parameterMetadata, + ProcedureParameterMetadataImplementor parameterMetadata, SessionFactoryImplementor sessionFactory) { this.parameterMetadata = parameterMetadata; this.sessionFactory = sessionFactory; } - public ProcedureParameterMetadataImpl getParameterMetadata() { + public ProcedureParameterMetadataImplementor getParameterMetadata() { return parameterMetadata; } @Override public boolean isBound(QueryParameterImplementor parameter) { - //noinspection SuspiciousMethodCalls - return bindingMap.containsKey( parameter ); + return parameter instanceof ProcedureParameterImplementor + && bindingMap.containsKey( parameter ); } @Override public

ProcedureParameterBinding

getBinding(QueryParameterImplementor

parameter) { - return getQueryParamerBinding( (ProcedureParameterImplementor

) parameter ); + return getQueryParameterBinding( (ProcedureParameterImplementor

) parameter ); } - public

ProcedureParameterBinding

getQueryParamerBinding(ProcedureParameterImplementor

parameter) { + public

ProcedureParameterBinding

getQueryParameterBinding(ProcedureParameterImplementor

parameter) { final var procParam = parameterMetadata.resolve( parameter ); - var binding = bindingMap.get( procParam ); + final var binding = bindingMap.get( procParam ); if ( binding == null ) { if ( !parameterMetadata.containsReference( parameter ) ) { throw new IllegalArgumentException( "Passed parameter is not registered with this query" ); } - binding = new ProcedureParameterBindingImpl<>( procParam, sessionFactory ); - bindingMap.put( procParam, binding ); + final var parameterBinding = new ProcedureParameterBindingImpl<>( procParam, sessionFactory ); + bindingMap.put( procParam, parameterBinding ); + return parameterBinding; + } + else { + //noinspection unchecked + return (ProcedureParameterBinding

) binding; } - //noinspection unchecked - return (ProcedureParameterBinding

) binding; } @Override public

ProcedureParameterBinding

getBinding(String name) { - //noinspection unchecked - final var parameter = - (ProcedureParameterImplementor

) - parameterMetadata.getQueryParameter( name ); + final var parameter = parameterMetadata.getQueryParameter( name ); if ( parameter == null ) { - throw new IllegalArgumentException( "Parameter does not exist: " + name ); + throw new IllegalArgumentException( "Parameter with name '" + name + "' does not exist" ); } - return getQueryParamerBinding( parameter ); + //noinspection unchecked + return getQueryParameterBinding( (ProcedureParameterImplementor

) parameter ); } @Override public

ProcedureParameterBinding

getBinding(int position) { - //noinspection unchecked - final var parameter = - (ProcedureParameterImplementor

) - parameterMetadata.getQueryParameter( position ); + final var parameter = parameterMetadata.getQueryParameter( position ); if ( parameter == null ) { throw new IllegalArgumentException( "Parameter at position " + position + "does not exist" ); } - return getQueryParamerBinding( parameter ); + //noinspection unchecked + return getQueryParameterBinding( (ProcedureParameterImplementor

) parameter ); } @Override public void validate() { - parameterMetadata.visitRegistrations( parameter -> validate( (ProcedureParameterImplementor) parameter ) ); + if ( LOG.isDebugEnabled() ) { + parameterMetadata.visitRegistrations( + parameter -> validate( (ProcedureParameterImplementor) parameter ) ); + } } private void validate(ProcedureParameterImplementor procParam) { - final ParameterMode mode = procParam.getMode(); + final var mode = procParam.getMode(); if ( mode == ParameterMode.IN || mode == ParameterMode.INOUT ) { if ( !getBinding( procParam ).isBound() ) { // depending on "pass nulls" this might be OK - for now, just log a warning - if ( procParam.getPosition() != null ) { + if ( procParam.isOrdinal() ) { LOG.debugf( "Procedure parameter at position %s is not bound", procParam.getPosition() ); } else { - LOG.debugf( "Procedure parameter %s is not bound", procParam.getName() ); + LOG.debugf( "Procedure parameter '%s' is not bound", procParam.getName() ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterBindingImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterBindingImpl.java index 8b1cf5a39536..cf80e1e8d8ee 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterBindingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterBindingImpl.java @@ -14,10 +14,10 @@ * * @author Steve Ebersole */ -public class ProcedureParameterBindingImpl +class ProcedureParameterBindingImpl extends QueryParameterBindingImpl implements ProcedureParameterBinding { - public ProcedureParameterBindingImpl( + ProcedureParameterBindingImpl( ProcedureParameterImplementor queryParameter, SessionFactoryImplementor sessionFactory) { super( queryParameter, sessionFactory ); diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterImpl.java index 4ba366ceef69..9bb664d6874d 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterImpl.java @@ -8,7 +8,6 @@ import java.util.Objects; import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData; -import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.procedure.ParameterTypeException; import org.hibernate.procedure.spi.NamedCallableQueryMemento; import org.hibernate.procedure.spi.ParameterStrategy; @@ -16,7 +15,6 @@ import org.hibernate.procedure.spi.ProcedureParameterImplementor; import org.hibernate.type.BindableType; import org.hibernate.type.OutputableType; -import org.hibernate.type.internal.BindingTypeHelper; import org.hibernate.query.spi.AbstractQueryParameter; import org.hibernate.query.spi.QueryParameterBinding; import org.hibernate.sql.exec.internal.JdbcCallParameterExtractorImpl; @@ -30,10 +28,12 @@ import jakarta.persistence.ParameterMode; +import static org.hibernate.type.internal.BindingTypeHelper.resolveTemporalPrecision; + /** * @author Steve Ebersole */ -public class ProcedureParameterImpl extends AbstractQueryParameter implements ProcedureParameterImplementor { +class ProcedureParameterImpl extends AbstractQueryParameter implements ProcedureParameterImplementor { private final String name; private final Integer position; @@ -43,7 +43,7 @@ public class ProcedureParameterImpl extends AbstractQueryParameter impleme /** * Used for named Query parameters */ - public ProcedureParameterImpl( + ProcedureParameterImpl( String name, ParameterMode mode, Class javaType, @@ -101,12 +101,16 @@ public NamedCallableQueryMemento.ParameterMemento toMemento() { public JdbcCallParameterRegistration toJdbcParameterRegistration( int startIndex, ProcedureCallImplementor procedureCall) { - final QueryParameterBinding binding = procedureCall.getParameterBindings().getBinding( this ); - final boolean isNamed = procedureCall.getParameterStrategy() == ParameterStrategy.NAMED && this.name != null; - final SharedSessionContractImplementor session = procedureCall.getSession(); + final QueryParameterBinding binding = + procedureCall.getParameterBindings() + .getBinding( this ); + final boolean isNamed = + procedureCall.getParameterStrategy() == ParameterStrategy.NAMED + && name != null; + final var session = procedureCall.getSession(); - final OutputableType typeToUse = (OutputableType) - BindingTypeHelper.resolveTemporalPrecision( + final var typeToUse = (OutputableType) + resolveTemporalPrecision( binding == null ? null : binding.getExplicitTemporalPrecision(), getBindableType( binding ), session.getFactory().getQueryEngine().getCriteriaBuilder() @@ -116,14 +120,20 @@ public JdbcCallParameterRegistration toJdbcParameterRegistration( final JdbcParameterBinder parameterBinder; final JdbcCallRefCursorExtractorImpl refCursorExtractor; final JdbcCallParameterExtractorImpl parameterExtractor; - final ExtractedDatabaseMetaData databaseMetaData = + final var databaseMetaData = session.getFactory().getJdbcServices().getJdbcEnvironment() .getExtractedDatabaseMetaData(); final boolean passProcedureParameterNames = - session.getFactory().getSessionFactoryOptions().isPassProcedureParameterNames(); + session.getFactory().getSessionFactoryOptions() + .isPassProcedureParameterNames(); switch ( mode ) { case REF_CURSOR: - jdbcParamName = this.name != null && databaseMetaData.supportsNamedParameters() && passProcedureParameterNames ? this.name : null; + jdbcParamName = + name != null + && databaseMetaData.supportsNamedParameters() + && passProcedureParameterNames + ? name + : null; refCursorExtractor = new JdbcCallRefCursorExtractorImpl( startIndex ); parameterBinder = null; parameterExtractor = null; @@ -151,12 +161,21 @@ public JdbcCallParameterRegistration toJdbcParameterRegistration( break; } - return new JdbcCallParameterRegistrationImpl( jdbcParamName, startIndex, mode, typeToUse, parameterBinder, parameterExtractor, refCursorExtractor ); + return new JdbcCallParameterRegistrationImpl( + jdbcParamName, + startIndex, + mode, + typeToUse, + parameterBinder, + parameterExtractor, + refCursorExtractor + ); } private BindableType getBindableType(QueryParameterBinding binding) { - if ( getHibernateType() != null ) { - return getHibernateType(); + final var type = getHibernateType(); + if ( type != null ) { + return type; } else if ( binding != null ) { //noinspection unchecked @@ -175,7 +194,7 @@ private String getJdbcParamName( ExtractedDatabaseMetaData databaseMetaData) { return isNamed && passProcedureParameterNames && canDoNameParameterBinding( typeToUse, procedureCall, databaseMetaData ) - ? this.name + ? name : null; } @@ -203,8 +222,8 @@ private JdbcParameterBinder getParameterBinder(BindableType typeToUse, String ) ); } - else if ( typeToUse instanceof BasicType ) { - return new JdbcParameterImpl( (BasicType) typeToUse ); + else if ( typeToUse instanceof BasicType basicType ) { + return new JdbcParameterImpl( basicType ); } else { throw new UnsupportedOperationException(); @@ -217,8 +236,8 @@ private boolean canDoNameParameterBinding( ExtractedDatabaseMetaData databaseMetaData) { return procedureCall.getFunctionReturn() == null && databaseMetaData.supportsNamedParameters() - && hibernateType instanceof ProcedureParameterNamedBinder - && ( (ProcedureParameterNamedBinder) hibernateType ).canDoSetting(); + && hibernateType instanceof ProcedureParameterNamedBinder binder + && binder.canDoSetting(); } @Override @@ -227,19 +246,21 @@ public int hashCode() { } @Override - public boolean equals(Object o) { - if ( this == o ) { + public boolean equals(Object object) { + if ( this == object ) { return true; } - if ( o == null ) { + else if ( object == null ) { return false; } - if ( !(o instanceof ProcedureParameterImpl that) ) { + else if ( !(object instanceof ProcedureParameterImpl that) ) { return false; } - return Objects.equals( name, that.name ) - && Objects.equals( position, that.position ) - && mode == that.mode; + else { + return Objects.equals( name, that.name ) + && Objects.equals( position, that.position ) + && mode == that.mode; + } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterMetadataImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterMetadataImpl.java index ac0d02d98462..69c406535a22 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterMetadataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterMetadataImpl.java @@ -14,8 +14,6 @@ import jakarta.persistence.Parameter; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.procedure.spi.NamedCallableQueryMemento; import org.hibernate.procedure.spi.ParameterStrategy; import org.hibernate.type.BindableType; import org.hibernate.query.QueryParameter; @@ -36,18 +34,19 @@ * * @author Steve Ebersole */ -public class ProcedureParameterMetadataImpl implements ProcedureParameterMetadataImplementor { +class ProcedureParameterMetadataImpl implements ProcedureParameterMetadataImplementor { private ParameterStrategy parameterStrategy = ParameterStrategy.UNKNOWN; private List> parameters; - public ProcedureParameterMetadataImpl() { + ProcedureParameterMetadataImpl() { } - public ProcedureParameterMetadataImpl(NamedCallableQueryMemento memento, SharedSessionContractImplementor session) { - memento.getParameterMementos() - .forEach( parameterMemento -> registerParameter( parameterMemento.resolve( session ) ) ); - } +// public ProcedureParameterMetadataImpl(NamedCallableQueryMemento memento, SharedSessionContractImplementor session) { +// memento.getParameterMementos() +// .forEach( parameterMemento -> registerParameter( parameterMemento.resolve( session ) ) ); +// } + @Override public void registerParameter(ProcedureParameterImplementor parameter) { if ( parameter.isNamed() ) { if ( parameterStrategy == ParameterStrategy.POSITIONAL ) { @@ -123,6 +122,7 @@ public boolean containsReference(QueryParameter parameter) { && parameters.contains( (ProcedureParameterImplementor) parameter ); } + @Override public ParameterStrategy getParameterStrategy() { return parameterStrategy; } diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ScalarDomainResultBuilder.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ScalarDomainResultBuilder.java index 240436770c6c..3c21c2a2987f 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ScalarDomainResultBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ScalarDomainResultBuilder.java @@ -20,10 +20,10 @@ /** * @author Steve Ebersole */ -public class ScalarDomainResultBuilder implements ResultBuilder { +class ScalarDomainResultBuilder implements ResultBuilder { private final JavaType typeDescriptor; - public ScalarDomainResultBuilder(JavaType typeDescriptor) { + ScalarDomainResultBuilder(JavaType typeDescriptor) { this.typeDescriptor = typeDescriptor; } diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/Util.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/Util.java index 9145aaf77bca..319ff33ac5ca 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/Util.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/Util.java @@ -6,15 +6,11 @@ import java.util.function.Consumer; -import org.hibernate.internal.util.collections.ArrayHelper; -import org.hibernate.metamodel.MappingMetamodel; -import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.UnknownSqlResultSetMappingException; import org.hibernate.query.internal.ResultSetMappingResolutionContext; -import org.hibernate.query.named.NamedObjectRepository; -import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.results.ResultSetMapping; -import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry; + +import static org.hibernate.internal.util.collections.ArrayHelper.isEmpty; /** * Utilities used to implement procedure call support. @@ -28,18 +24,18 @@ private Util() { public static void resolveResultSetMappings( String[] resultSetMappingNames, - Class[] resultSetMappingClasses, + Class[] resultSetMappingClasses, ResultSetMapping resultSetMapping, Consumer querySpaceConsumer, ResultSetMappingResolutionContext context) { - if ( ! ArrayHelper.isEmpty( resultSetMappingNames ) ) { + if ( !isEmpty( resultSetMappingNames ) ) { // cannot specify both - if ( ! ArrayHelper.isEmpty( resultSetMappingClasses ) ) { + if ( !isEmpty( resultSetMappingClasses ) ) { throw new IllegalArgumentException( "Cannot specify both result-set mapping names and classes" ); } resolveResultSetMappingNames( resultSetMappingNames, resultSetMapping, querySpaceConsumer, context ); } - else if ( ! ArrayHelper.isEmpty( resultSetMappingClasses ) ) { + else if ( !isEmpty( resultSetMappingClasses ) ) { resolveResultSetMappingClasses( resultSetMappingClasses, resultSetMapping, querySpaceConsumer, context ); } @@ -51,31 +47,26 @@ public static void resolveResultSetMappingNames( ResultSetMapping resultSetMapping, Consumer querySpaceConsumer, ResultSetMappingResolutionContext context) { - final NamedObjectRepository namedObjectRepository = context.getNamedObjectRepository(); + final var namedObjectRepository = context.getNamedObjectRepository(); for ( String resultSetMappingName : resultSetMappingNames ) { - final NamedResultSetMappingMemento memento = + final var memento = namedObjectRepository.getResultSetMappingMemento( resultSetMappingName ); if ( memento == null ) { throw new UnknownSqlResultSetMappingException( "Unknown SqlResultSetMapping [" + resultSetMappingName + "]" ); } - memento.resolve( - resultSetMapping, - querySpaceConsumer, - context - ); + memento.resolve( resultSetMapping, querySpaceConsumer, context ); } } public static void resolveResultSetMappingClasses( - Class[] resultSetMappingClasses, + Class[] resultSetMappingClasses, ResultSetMapping resultSetMapping, Consumer querySpaceConsumer, ResultSetMappingResolutionContext context) { - final MappingMetamodel mappingMetamodel = context.getMappingMetamodel(); - final JavaTypeRegistry javaTypeRegistry = mappingMetamodel.getTypeConfiguration().getJavaTypeRegistry(); - - for ( Class resultSetMappingClass : resultSetMappingClasses ) { - final EntityPersister entityDescriptor = mappingMetamodel.findEntityDescriptor( resultSetMappingClass ); + final var mappingMetamodel = context.getMappingMetamodel(); + final var javaTypeRegistry = mappingMetamodel.getTypeConfiguration().getJavaTypeRegistry(); + for ( var resultSetMappingClass : resultSetMappingClasses ) { + final var entityDescriptor = mappingMetamodel.findEntityDescriptor( resultSetMappingClass ); if ( entityDescriptor != null ) { resultSetMapping.addResultBuilder( new EntityDomainResultBuilder( entityDescriptor ) ); for ( String querySpace : entityDescriptor.getSynchronizedQuerySpaces() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/ProcedureParameterMetadataImplementor.java b/hibernate-core/src/main/java/org/hibernate/query/spi/ProcedureParameterMetadataImplementor.java index 4382ae09c303..25c86cac9e96 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/ProcedureParameterMetadataImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/ProcedureParameterMetadataImplementor.java @@ -6,9 +6,25 @@ import java.util.List; +import jakarta.persistence.Parameter; +import org.hibernate.procedure.spi.ParameterStrategy; import org.hibernate.procedure.spi.ProcedureParameterImplementor; public interface ProcedureParameterMetadataImplementor extends ParameterMetadataImplementor { + ParameterStrategy getParameterStrategy(); + + @Override + ProcedureParameterImplementor getQueryParameter(String name); + + @Override + ProcedureParameterImplementor getQueryParameter(int positionLabel); + + @Override +

ProcedureParameterImplementor

resolve(Parameter

parameter); + + void registerParameter(ProcedureParameterImplementor parameter); + List> getRegistrationsAsList(); + } From 5874eeffd8ac44be52c8edb5b79c9826b9c730ec Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 5 Dec 2025 10:35:51 +0100 Subject: [PATCH 022/181] fix lots of NPEs in ProcedureParameterMetadataImpl these could happen when no parameter was registered due to the optimization of lazily initializing the list of parameters --- .../ProcedureParameterMetadataImpl.java | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterMetadataImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterMetadataImpl.java index 69c406535a22..cd77b4e06cba 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterMetadataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterMetadataImpl.java @@ -18,7 +18,6 @@ import org.hibernate.type.BindableType; import org.hibernate.query.QueryParameter; import org.hibernate.query.internal.QueryParameterBindingsImpl; -import org.hibernate.procedure.ProcedureParameter; import org.hibernate.procedure.spi.ProcedureParameterImplementor; import org.hibernate.query.spi.ProcedureParameterMetadataImplementor; import org.hibernate.query.spi.QueryParameterBindings; @@ -38,14 +37,6 @@ class ProcedureParameterMetadataImpl implements ProcedureParameterMetadataImplem private ParameterStrategy parameterStrategy = ParameterStrategy.UNKNOWN; private List> parameters; - ProcedureParameterMetadataImpl() { - } - -// public ProcedureParameterMetadataImpl(NamedCallableQueryMemento memento, SharedSessionContractImplementor session) { -// memento.getParameterMementos() -// .forEach( parameterMemento -> registerParameter( parameterMemento.resolve( session ) ) ); -// } - @Override public void registerParameter(ProcedureParameterImplementor parameter) { if ( parameter.isNamed() ) { @@ -98,17 +89,18 @@ public boolean hasPositionalParameters() { @Override public Set getNamedParameterNames() { - if ( !hasNamedParameters() ) { - return emptySet(); - } - - final Set rtn = new HashSet<>(); - for ( ProcedureParameter parameter : parameters ) { - if ( parameter.getName() != null ) { - rtn.add( parameter.getName() ); + if ( hasNamedParameters() && parameters != null ) { + final Set names = new HashSet<>(); + for ( var parameter : parameters ) { + if ( parameter.getName() != null ) { + names.add( parameter.getName() ); + } } + return names; + } + else { + return emptySet(); } - return rtn; } @Override @@ -129,7 +121,7 @@ public ParameterStrategy getParameterStrategy() { @Override public boolean hasAnyMatching(Predicate> filter) { - if ( parameters.isEmpty() ) { + if ( parameters == null || parameters.isEmpty() ) { return false; } else { @@ -144,9 +136,11 @@ public boolean hasAnyMatching(Predicate> filter) { @Override public ProcedureParameterImplementor findQueryParameter(String name) { - for ( var parameter : parameters ) { - if ( name.equals( parameter.getName() ) ) { - return parameter; + if ( parameters != null ) { + for ( var parameter : parameters ) { + if ( name.equals( parameter.getName() ) ) { + return parameter; + } } } return null; @@ -163,9 +157,12 @@ public ProcedureParameterImplementor getQueryParameter(String name) { @Override public ProcedureParameterImplementor findQueryParameter(int positionLabel) { - for ( var parameter : parameters ) { - if ( parameter.getName() == null && positionLabel == parameter.getPosition() ) { - return parameter; + if ( parameters != null ) { + for ( var parameter : parameters ) { + if ( parameter.getName() == null + && positionLabel == parameter.getPosition() ) { + return parameter; + } } } return null; @@ -174,18 +171,20 @@ public ProcedureParameterImplementor findQueryParameter(int positionLabel) { @Override public ProcedureParameterImplementor getQueryParameter(int positionLabel) { final var queryParameter = findQueryParameter( positionLabel ); - if ( queryParameter != null ) { - return queryParameter; + if ( queryParameter == null ) { + throw new IllegalArgumentException( + "Positional parameter " + positionLabel + " is not registered with this procedure call" ); } - throw new IllegalArgumentException( "Positional parameter [" + positionLabel + "] is not registered with this procedure call" ); + return queryParameter; } @Override public

ProcedureParameterImplementor

resolve(Parameter

parameter) { - if ( parameter instanceof ProcedureParameterImplementor

parameterImplementor ) { + if ( parameters != null + && parameter instanceof ProcedureParameterImplementor

procedureParam ) { for ( var registered : parameters ) { if ( registered == parameter ) { - return parameterImplementor; + return procedureParam; } } } From c893708fe8bfe9eb9bd52af0ffcfe90e1ebb3653 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 5 Dec 2025 11:15:42 +0100 Subject: [PATCH 023/181] fix annoying assertions that test error message text --- .../procedure/MySQLStoredProcedureTest.java | 5 +--- .../PostgreSQLFunctionProcedureTest.java | 29 ++++++++++--------- .../PostgreSQLStoredProcedureTest.java | 20 +++++-------- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/MySQLStoredProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/MySQLStoredProcedureTest.java index 68e09cc4e0af..0879dfab9c80 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/MySQLStoredProcedureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/MySQLStoredProcedureTest.java @@ -408,10 +408,7 @@ public void testStoredProcedureNullParameterHibernateWithoutSettingTheParameter( fail( "Should have thrown exception" ); } catch (IllegalArgumentException e) { - assertEquals( - "The parameter at position [1] was not set! You need to call the setParameter method.", - e.getMessage() - ); + assertTrue( e.getMessage().contains( "parameter at position 1" ) ); } } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLFunctionProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLFunctionProcedureTest.java index 0aef95778b52..db9cc4f187af 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLFunctionProcedureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLFunctionProcedureTest.java @@ -35,8 +35,10 @@ import java.time.ZoneOffset; import java.util.Set; -import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; /** @@ -186,7 +188,7 @@ public void testFunctionProcedureOutParameter() { query.execute(); Long phoneCount = (Long) query.getSingleResult(); - assertThat( phoneCount ).isEqualTo( 2 ); + assertEquals( 2, phoneCount ); } ); } @@ -199,7 +201,7 @@ public void testFunctionProcedureRefCursor() { query.setParameter( 1, 1L ); - assertThat( query.getResultList() ).hasSize( 2 ); + assertEquals( 2, query.getResultList().size() ); } ); } @@ -213,7 +215,7 @@ public void testFunctionProcedureRefCursorOld() { query.setParameter( 2, 1L ); - assertThat( query.getResultList() ).hasSize( 2 ); + assertEquals( 2, query.getResultList().size() ); } ); } @@ -236,7 +238,7 @@ public void testFunctionWithJDBC() { } } } ); - assertThat( phoneCount ).isEqualTo( 2 ); + assertEquals( 2, phoneCount ); } ); } @@ -270,7 +272,7 @@ public void testSysRefCursorAsOutParameter() { catch (Exception e) { fail( e.getMessage() ); } - assertThat( value ).isEqualTo( 1 ); + assertEquals( 1, value ); StoredProcedureQuery function = entityManager.createStoredProcedureQuery( "singleRefCursor" ); @@ -280,7 +282,7 @@ public void testSysRefCursorAsOutParameter() { value = (Integer) function.getSingleResult(); - assertThat( value ).isEqualTo( 1 ); + assertEquals( 1, value ); } ); } @@ -294,7 +296,7 @@ public void testSysRefCursorAsOutParameterOld() { function.execute(); - assertThat( function.hasMoreResults() ).isFalse(); + assertFalse( function.hasMoreResults() ); Integer value = null; try (ResultSet resultSet = (ResultSet) function.getOutputParameterValue( 1 )) { @@ -306,7 +308,7 @@ public void testSysRefCursorAsOutParameterOld() { fail( e.getMessage() ); } - assertThat( value ).isEqualTo( 1 ); + assertEquals( 1, value ); } ); } @@ -323,7 +325,7 @@ public void testFunctionProcedureNullParameterHibernate() { Boolean result = (Boolean) procedureCall.getSingleResult(); - assertThat( result ).isTrue(); + assertTrue( result ); } ); inTransaction( entityManager -> { @@ -335,7 +337,7 @@ public void testFunctionProcedureNullParameterHibernate() { Boolean result = (Boolean) procedureCall.getSingleResult(); - assertThat( result ).isFalse(); + assertFalse( result ); } ); } @@ -352,7 +354,7 @@ public void testFunctionProcedureNullParameterHibernateWithoutEnablePassingNulls Boolean result = (Boolean) procedureCall.getSingleResult(); - assertThat( result ).isTrue(); + assertTrue( result ); } ); } @@ -369,7 +371,6 @@ public void testFunctionProcedureNullParameterHibernateWithoutSettingTheParamete procedureCall.execute(); } ) ); - assertThat( exception.getMessage() ) - .isEqualTo( "The parameter named [param] was not set! You need to call the setParameter method." ); + assertTrue( exception.getMessage().contains( "parameter named 'param'" ) ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLStoredProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLStoredProcedureTest.java index 60befa8191cf..f25da5e7dd43 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLStoredProcedureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/PostgreSQLStoredProcedureTest.java @@ -26,7 +26,6 @@ import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.Setting; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -39,10 +38,10 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /** * @author Vlad Mihalcea @@ -230,10 +229,7 @@ public void testStoredProcedureNullParameterHibernateWithoutSettingTheParameter( fail( "Should have thrown exception" ); } catch (IllegalArgumentException e) { - assertEquals( - "The parameter named [param] was not set! You need to call the setParameter method.", - e.getMessage() - ); + assertTrue( e.getMessage().contains( "parameter named 'param'" ) ); } } ); } @@ -253,7 +249,7 @@ public void testStoredProcedureInAndOutAndRefCursorParameters(EntityManagerFacto query.execute(); ResultSet rs = (ResultSet) query.getOutputParameterValue( "rec_out" ); try { - Assertions.assertTrue( rs.next() ); + assertTrue( rs.next() ); assertThat( rs.getString( "street" ), is( STREET ) ); assertThat( rs.getString( "city" ), is( CITY ) ); assertThat( rs.getString( "zip" ), is( ZIP ) ); @@ -280,7 +276,7 @@ public void testStoredProcedureInAndOutAndRefCursorParametersDifferentRegistrati query.execute(); ResultSet rs = (ResultSet) query.getOutputParameterValue( "rec_out" ); try { - Assertions.assertTrue( rs.next() ); + assertTrue( rs.next() ); assertThat( rs.getString( "street" ), is( STREET ) ); assertThat( rs.getString( "city" ), is( CITY ) ); assertThat( rs.getString( "zip" ), is( ZIP ) ); @@ -307,7 +303,7 @@ public void testStoredProcedureInAndOutAndRefCursorParametersDifferentRegistrati query.execute(); ResultSet rs = (ResultSet) query.getOutputParameterValue( "rec_out" ); try { - Assertions.assertTrue( rs.next() ); + assertTrue( rs.next() ); assertThat( rs.getString( "street" ), is( STREET ) ); assertThat( rs.getString( "city" ), is( CITY ) ); assertThat( rs.getString( "zip" ), is( ZIP ) ); From 333dd4e0987e2774e03924484b279f913eb12ee7 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 5 Dec 2025 11:58:38 +0100 Subject: [PATCH 024/181] just use a loop --- .../org/hibernate/procedure/internal/ProcedureCallImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java index 3cc5a614cee4..3a7d21178c7d 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java @@ -129,8 +129,9 @@ private static String mappingId(String procedureName, String[] resultSetMappingN } private void registerParameters(SharedSessionContractImplementor session, NamedCallableQueryMemento memento) { - memento.getParameterMementos() - .forEach( parameterMemento -> registerParameter( parameterMemento.resolve( session ) ) ); + for ( var parameterMemento : memento.getParameterMementos() ) { + registerParameter( parameterMemento.resolve( session ) ); + } } private ProcedureCallImpl( From 8213cca58bd8b77b7b696aff8ff9b3a39ed1ba64 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 5 Dec 2025 13:41:18 +0100 Subject: [PATCH 025/181] improve jdoc of SS.xxxxMultiple() methods --- .../java/org/hibernate/StatelessSession.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/StatelessSession.java b/hibernate-core/src/main/java/org/hibernate/StatelessSession.java index 17a968aea794..e60fa236161d 100644 --- a/hibernate-core/src/main/java/org/hibernate/StatelessSession.java +++ b/hibernate-core/src/main/java/org/hibernate/StatelessSession.java @@ -97,7 +97,9 @@ public interface StatelessSession extends SharedSessionContract { Object insert(Object entity); /** - * Insert multiple records. + * Insert multiple records in the same order as the entity + * instances representing the new records occur in the given + * list. * * @param entities a list of transient instances to be inserted * @@ -130,7 +132,9 @@ public interface StatelessSession extends SharedSessionContract { void update(Object entity); /** - * Update multiple records. + * Update multiple records in the same order as the entity + * instances representing the records occur in the given + * list. * * @param entities a list of detached instances to be updated * @@ -161,7 +165,9 @@ public interface StatelessSession extends SharedSessionContract { void delete(Object entity); /** - * Delete multiple records. + * Delete multiple records in the same order as the entity + * instances representing the records occur in the given + * list. * * @param entities a list of detached instances to be deleted * @@ -206,9 +212,11 @@ public interface StatelessSession extends SharedSessionContract { void upsert(Object entity); /** - * Perform an upsert, that is, to insert the record if it does - * not exist, or update the record if it already exists, for - * each given record. + * Upsert multiple records, that is, for a given record, + * insert the record if it does not exist or update the + * record if it already exists, in the same order as the + * entity instances representing the records occur in + * the given list. * * @param entities a list of detached instances and new * instances with assigned identifiers From 84b37758ce970390b0c797d7efed81c11e60388a Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 3 Dec 2025 15:41:35 +0100 Subject: [PATCH 026/181] HHH-19943 Add test for issue --- .../generics/embeddedid/BaseEntity.java | 32 ++++++++ .../generics/embeddedid/EmployeeEntity.java | 20 +++++ .../generics/embeddedid/EmployeeId.java | 29 +++++++ .../GenericEmbeddedIdMetamodelTest.java | 79 +++++++++++++++++++ .../generics/embeddedid/PersonEntity.java | 17 ++++ .../generics/embeddedid/PersonId.java | 23 ++++++ 6 files changed, 200 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/BaseEntity.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/EmployeeEntity.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/EmployeeId.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/GenericEmbeddedIdMetamodelTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/PersonEntity.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/PersonId.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/BaseEntity.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/BaseEntity.java new file mode 100644 index 000000000000..7c6e8226e841 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/BaseEntity.java @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.metamodel.generics.embeddedid; + +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +public abstract class BaseEntity { + @EmbeddedId + private ID id; + + private String name; + + public BaseEntity() { + } + + public BaseEntity(ID id, String name) { + this.id = id; + this.name = name; + } + + public ID getId() { + return id; + } + + public String getName() { + return name; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/EmployeeEntity.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/EmployeeEntity.java new file mode 100644 index 000000000000..7038062ff059 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/EmployeeEntity.java @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.metamodel.generics.embeddedid; + +import jakarta.persistence.Entity; + +@Entity +public class EmployeeEntity extends BaseEntity { + private Integer employeeNumber; + + public EmployeeEntity() { + } + + public EmployeeEntity(EmployeeId id, String name, Integer employeeNumber) { + super( id, name ); + this.employeeNumber = employeeNumber; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/EmployeeId.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/EmployeeId.java new file mode 100644 index 000000000000..763f5ff8d8ce --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/EmployeeId.java @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.metamodel.generics.embeddedid; + +import jakarta.persistence.Embeddable; + +@Embeddable +public class EmployeeId { + private String employeeCode; + private PersonId personId; + + public EmployeeId() { + } + + public EmployeeId(String employeeCode, PersonId personId) { + this.employeeCode = employeeCode; + this.personId = personId; + } + + public String getEmployeeCode() { + return employeeCode; + } + + public PersonId getPersonId() { + return personId; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/GenericEmbeddedIdMetamodelTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/GenericEmbeddedIdMetamodelTest.java new file mode 100644 index 000000000000..e4725a52d15c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/GenericEmbeddedIdMetamodelTest.java @@ -0,0 +1,79 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.metamodel.generics.embeddedid; + +import jakarta.persistence.criteria.Path; +import org.hibernate.SessionFactory; +import org.hibernate.query.sqm.tree.domain.SqmPath; +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Marco Belladelli + */ +@Jpa(annotatedClasses = { + BaseEntity.class, + PersonEntity.class, + PersonId.class, + EmployeeEntity.class, + EmployeeId.class, +}) +public class GenericEmbeddedIdMetamodelTest { + @SuppressWarnings({"unchecked", "rawtypes"}) + @Test + public void test(EntityManagerFactoryScope scope) { + scope.inTransaction( entityManager -> { + var criteriaBuilder = entityManager.getCriteriaBuilder(); + var query = criteriaBuilder.createQuery( EmployeeEntity.class ); + + var employee = query.from( EmployeeEntity.class ); + var person = query.from( PersonEntity.class ); + + final Path employeeId = employee.get( EmployeeEntity_.id ); + // The Path.getModel() method returns the generic (Object) type, whereas our getResolvedModel() + // returns the correct EmployeeId type. + assertThat( employeeId.getModel().getBindableJavaType() ).isEqualTo( Object.class ); + assertThat( ((SqmPath) employeeId).getResolvedModel().getBindableJavaType() ).isEqualTo( EmployeeId.class ); + final Path employeePersonId = employeeId.get( EmployeeId_.personId ); + assertThat( employeePersonId.getModel().getBindableJavaType() ).isEqualTo( PersonId.class ); + final var personId = person.get( PersonEntity_.id ); + // Same as before: generic vs resolved type + assertThat( personId.getModel().getBindableJavaType() ).isEqualTo( Object.class ); + assertThat( ((SqmPath) employeeId).getResolvedModel().getBindableJavaType() ).isEqualTo( EmployeeId.class ); + + var equal = criteriaBuilder.equal( + employeePersonId, + personId + ); + + query.select( employee ).where( equal ); + + final var result = entityManager.createQuery( query ).getSingleResult(); + assertThat( result.getName() ).isEqualTo( "Employee One" ); + assertThat( result.getId().getPersonId().getIdentifier() ).isEqualTo( 1L ); + } ); + } + + @BeforeAll + public void setUp(EntityManagerFactoryScope scope) { + scope.inTransaction( entityManager -> { + entityManager.persist( new PersonEntity( new PersonId( 1L ), "Person One" ) ); + entityManager.persist( new PersonEntity( new PersonId( 2L ), "Person Two" ) ); + entityManager.persist( + new EmployeeEntity( new EmployeeId( "E001", new PersonId( 1L ) ), "Employee One", 1001 ) ); + } ); + } + + @AfterAll + public void tearDown(EntityManagerFactoryScope scope) { + scope.getEntityManagerFactory().unwrap( SessionFactory.class ).getSchemaManager().truncateMappedObjects(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/PersonEntity.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/PersonEntity.java new file mode 100644 index 000000000000..1990aa9996a6 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/PersonEntity.java @@ -0,0 +1,17 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.metamodel.generics.embeddedid; + +import jakarta.persistence.Entity; + +@Entity +public class PersonEntity extends BaseEntity { + public PersonEntity() { + } + + public PersonEntity(PersonId id, String name) { + super( id, name ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/PersonId.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/PersonId.java new file mode 100644 index 000000000000..0420dabe3c9f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/generics/embeddedid/PersonId.java @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.metamodel.generics.embeddedid; + +import jakarta.persistence.Embeddable; + +@Embeddable +public class PersonId { + private Long identifier; + + public PersonId() { + } + + public PersonId(Long identifier) { + this.identifier = identifier; + } + + public Long getIdentifier() { + return identifier; + } +} From 95c7bc6674f65af0742501d534b9656a5159b9b3 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 3 Dec 2025 15:41:45 +0100 Subject: [PATCH 027/181] HHH-19943 Use resolved type for `SqmSelectableNode#getTupeLength` --- .../org/hibernate/query/sqm/tree/select/SqmSelectableNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectableNode.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectableNode.java index 211d22610866..d291aa6f5ac9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectableNode.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectableNode.java @@ -35,7 +35,7 @@ public interface SqmSelectableNode extends JpaSelection, SqmTypedNode { SqmSelectableNode copy(SqmCopyContext context); default @Nullable Integer getTupleLength() { - final SqmBindableType nodeType = getNodeType(); + final SqmBindableType nodeType = getExpressible(); final SqmDomainType sqmType = nodeType == null ? null : nodeType.getSqmType(); return sqmType == null ? 1 : sqmType.getTupleLength(); } From bdcbd99e10963dcc6549b541205711947ae2ba9b Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 5 Dec 2025 19:50:28 +0100 Subject: [PATCH 028/181] fill in impl of methods of EntityJavaType this is sorta useful with SQL result set mappings --- .../descriptor/java/spi/EntityJavaType.java | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java index 07114c69110f..a4304068fafe 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java @@ -21,14 +21,17 @@ public class EntityJavaType extends AbstractClassJavaType { public EntityJavaType(Class type, MutabilityPlan mutabilityPlan) { - super( type, mutabilityPlan , IncomparableComparator.INSTANCE ); + super( type, mutabilityPlan, IncomparableComparator.INSTANCE ); } @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { - throw new JdbcTypeRecommendationException( - "Could not determine recommended JdbcType for '" + getTypeName() + "'" - ); + return context.getTypeConfiguration().getSessionFactory() + .getMappingMetamodel() + .getEntityDescriptor(getJavaTypeClass()) + .getIdentifierDescriptor() + .getJavaType() + .getRecommendedJdbcType( context ); } @Override @@ -66,18 +69,31 @@ public T fromString(CharSequence string) { ); } - @Override + @Override @SuppressWarnings("unchecked") // safe, we do check public X unwrap(T value, Class type, WrapperOptions options) { - throw new UnsupportedOperationException( - "Unwrap strategy not known for this Java type: " + getTypeName() - ); + final var id = + options.getSessionFactory().getMappingMetamodel() + .getEntityDescriptor( getJavaTypeClass() ) + .getIdentifier( value ); + if ( !type.isInstance( id ) ) { + throw new IllegalArgumentException( "Id not an instance of type " + type.getName() ); + } + return (X) value; } - @Override + @Override @SuppressWarnings("unchecked") // safe, we do check public T wrap(X value, WrapperOptions options) { - throw new UnsupportedOperationException( - "Wrap strategy not known for this Java type: " + getTypeName() - ); + final var persister = + options.getSessionFactory().getMappingMetamodel() + .getEntityDescriptor( getJavaTypeClass() ); + final var idType = persister.getIdentifierType().getReturnedClass(); + if ( !idType.isInstance( value ) ) { + throw new IllegalArgumentException( "Not an instance of id type " + idType.getName() ); + } + final var entity = + options.getSession() + .internalLoad( persister.getEntityName(), value, false, true ); + return (T) entity; } @Override From b0016bc89c95404ec252989fdbbe12dfb6c52ea3 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 5 Dec 2025 19:50:51 +0100 Subject: [PATCH 029/181] remove unnecessary LOG in Table --- .../java/org/hibernate/mapping/Table.java | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java index 04d3f60d8a6b..79e2a29d16bc 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -30,7 +30,6 @@ import org.hibernate.dialect.Dialect; import org.hibernate.resource.transaction.spi.DdlTransactionIsolator; -import org.jboss.logging.Logger; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; @@ -46,7 +45,6 @@ * @author Gavin King */ public class Table implements Serializable, ContributableDatabaseObject { - private static final Logger LOG = Logger.getLogger( Table.class ); private static final Column[] EMPTY_COLUMN_ARRAY = new Column[0]; private final String contributor; @@ -110,8 +108,9 @@ public Table( String subselect, boolean isAbstract) { this.contributor = contributor; - this.catalog = namespace.getPhysicalName().catalog(); - this.schema = namespace.getPhysicalName().schema(); + final var physicalName = namespace.getPhysicalName(); + this.catalog = physicalName.catalog(); + this.schema = physicalName.schema(); this.name = physicalTableName; this.subselect = subselect; this.isAbstract = isAbstract; @@ -119,8 +118,9 @@ public Table( public Table(String contributor, Namespace namespace, String subselect, boolean isAbstract) { this.contributor = contributor; - this.catalog = namespace.getPhysicalName().catalog(); - this.schema = namespace.getPhysicalName().schema(); + final var physicalName = namespace.getPhysicalName(); + this.catalog = physicalName.catalog(); + this.schema = physicalName.schema(); this.subselect = subselect; this.isAbstract = isAbstract; } @@ -247,7 +247,7 @@ public Column getColumn(Column column) { return null; } else { - final Column existing = columns.get( column.getCanonicalName() ); + final var existing = columns.get( column.getCanonicalName() ); return column.equals( existing ) ? existing : null; } } @@ -276,15 +276,11 @@ public void addColumn(Column column) { if ( oldColumn == null ) { if ( primaryKey != null ) { for ( var primaryKeyColumn : primaryKey.getColumns() ) { - if ( primaryKeyColumn.getCanonicalName().equals( column.getCanonicalName() ) ) { + if ( Objects.equals( column.getCanonicalName(), + primaryKeyColumn.getCanonicalName() ) ) { + // Force the column to be non-null + // as it is part of the primary key column.setNullable( false ); - if ( LOG.isTraceEnabled() ) { - LOG.tracef( - "Forcing column [%s] to be non-null as it is part of the primary key for table [%s]", - column.getCanonicalName(), - getNameIdentifier().getCanonicalName() - ); - } } } } @@ -452,18 +448,21 @@ public void setPrimaryKey(PrimaryKey primaryKey) { } public Index getOrCreateIndex(String indexName) { - Index index = indexes.get( indexName ); - if ( index == null ) { - index = new Index(); - index.setName( indexName ); - index.setTable( this ); - indexes.put( indexName, index ); + final var index = indexes.get( indexName ); + if ( index != null ) { + return index; + } + else { + final var newIndex = new Index(); + newIndex.setName( indexName ); + newIndex.setTable( this ); + indexes.put( indexName, newIndex ); + return newIndex; } - return index; } public Index getIndex(String indexName) { - return indexes.get( indexName ); + return indexes.get( indexName ); } public Index addIndex(Index index) { From d4b83dd350561cb33da1373434f23fa8f92eb9b5 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 5 Dec 2025 20:08:52 +0100 Subject: [PATCH 030/181] code cleanups in sql.results.jdbc.internal mostly just lots of var --- .../jdbc/internal/AbstractJdbcValues.java | 18 ++-- .../internal/AbstractResultSetAccess.java | 11 ++- .../internal/CachedJdbcValuesMetadata.java | 13 ++- .../internal/DeferredResultSetAccess.java | 88 ++++++++++--------- .../jdbc/internal/DirectResultSetAccess.java | 4 +- .../jdbc/internal/JdbcValuesCacheHit.java | 60 ++++++------- .../JdbcValuesMappingProducerStandard.java | 4 +- .../JdbcValuesMappingResolutionImpl.java | 7 +- .../internal/JdbcValuesResultSetImpl.java | 34 ++++--- ...luesSourceProcessingStateStandardImpl.java | 19 +--- .../internal/StandardJdbcValuesMapping.java | 32 +++---- 11 files changed, 139 insertions(+), 151 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractJdbcValues.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractJdbcValues.java index 26f6f835cdcf..1433fa7383e7 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractJdbcValues.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractJdbcValues.java @@ -21,9 +21,9 @@ public final boolean next(RowProcessingState rowProcessingState) { @Override public boolean previous(RowProcessingState rowProcessingState) { - // NOTE : we do not even bother interacting with the query-cache put manager because - // this method is implicitly related to scrolling and caching of scrolled results - // is not supported + // NOTE: we do not even bother interacting with the query-cache put manager because + // this method is implicitly related to scrolling and caching of scrolled results + // is not supported return processPrevious( rowProcessingState ); } @@ -31,9 +31,9 @@ public boolean previous(RowProcessingState rowProcessingState) { @Override public boolean scroll(int numberOfRows, RowProcessingState rowProcessingState) { - // NOTE : we do not even bother interacting with the query-cache put manager because - // this method is implicitly related to scrolling and caching of scrolled results - // is not supported + // NOTE: we do not even bother interacting with the query-cache put manager because + // this method is implicitly related to scrolling and caching of scrolled results + // is not supported return processScroll( numberOfRows, rowProcessingState ); } @@ -41,9 +41,9 @@ public boolean scroll(int numberOfRows, RowProcessingState rowProcessingState) { @Override public boolean position(int position, RowProcessingState rowProcessingState) { - // NOTE : we do not even bother interacting with the query-cache put manager because - // this method is implicitly related to scrolling and caching of scrolled results - // is not supported + // NOTE: we do not even bother interacting with the query-cache put manager because + // this method is implicitly related to scrolling and caching of scrolled results + // is not supported return processPosition( position, rowProcessingState ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractResultSetAccess.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractResultSetAccess.java index 99184c081997..800fe88ef3a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractResultSetAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractResultSetAccess.java @@ -17,7 +17,6 @@ import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; -import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; /** @@ -108,18 +107,18 @@ public int getResultCountEstimate() { @Override public BasicType resolveType(int position, JavaType explicitJavaType, TypeConfiguration typeConfiguration) { try { - final ResultSetMetaData metaData = getResultSetMetaData(); - final JdbcTypeRegistry registry = typeConfiguration.getJdbcTypeRegistry(); + final var metaData = getResultSetMetaData(); + final var registry = typeConfiguration.getJdbcTypeRegistry(); final String columnTypeName = metaData.getColumnTypeName( position ); final int columnType = metaData.getColumnType( position ); final int scale = metaData.getScale( position ); final int precision = metaData.getPrecision( position ); final int displaySize = metaData.getColumnDisplaySize( position ); - final Dialect dialect = getDialect(); + final var dialect = getDialect(); final int length = dialect.resolveSqlTypeLength( columnTypeName, columnType, precision, scale, displaySize ); - final JdbcType resolvedJdbcType = + final var resolvedJdbcType = dialect.resolveSqlTypeDescriptor( columnTypeName, columnType, length, scale, registry ); - final JdbcType jdbcType = + final var jdbcType = explicitJavaType == null ? resolvedJdbcType : jdbcType( explicitJavaType, resolvedJdbcType, length, precision, scale, typeConfiguration ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/CachedJdbcValuesMetadata.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/CachedJdbcValuesMetadata.java index ba45c9873eb1..8d07a3a374a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/CachedJdbcValuesMetadata.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/CachedJdbcValuesMetadata.java @@ -6,12 +6,13 @@ import java.io.Serializable; -import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata; import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.spi.TypeConfiguration; +import static org.hibernate.internal.util.collections.ArrayHelper.indexOf; + public final class CachedJdbcValuesMetadata implements JdbcValuesMetadata, Serializable { private final String[] columnNames; private final BasicType[] types; @@ -28,7 +29,7 @@ public int getColumnCount() { @Override public int resolveColumnPosition(String columnName) { - final int position = ArrayHelper.indexOf( columnNames, columnName ) + 1; + final int position = indexOf( columnNames, columnName ) + 1; if ( position == 0 ) { throw new IllegalStateException( "Unexpected resolving of unavailable column: " + columnName ); } @@ -49,7 +50,7 @@ public BasicType resolveType( int position, JavaType explicitJavaType, TypeConfiguration typeConfiguration) { - final BasicType type = types[position - 1]; + final var type = types[position - 1]; if ( type == null ) { throw new IllegalStateException( "Unexpected resolving of unavailable column at position: " + position ); } @@ -58,10 +59,8 @@ public BasicType resolveType( return (BasicType) type; } else { - return typeConfiguration.getBasicTypeRegistry().resolve( - explicitJavaType, - type.getJdbcType() - ); + return typeConfiguration.getBasicTypeRegistry() + .resolve( explicitJavaType, type.getJdbcType() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java index fde5c29cf3ce..4a2060226842 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java @@ -9,14 +9,10 @@ import org.hibernate.dialect.Dialect; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.NoopLimitHandler; -import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.SqlStatementLogger; import org.hibernate.engine.spi.SessionEventListenerManager; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.event.monitor.spi.DiagnosticEvent; -import org.hibernate.event.monitor.spi.EventMonitor; import org.hibernate.query.spi.Limit; import org.hibernate.query.spi.QueryOptions; import org.hibernate.resource.jdbc.spi.JdbcSessionContext; @@ -62,7 +58,7 @@ public DeferredResultSetAccess( JdbcSelectExecutor.StatementCreator statementCreator, int resultCountEstimate) { super( executionContext.getSession() ); - final JdbcServices jdbcServices = executionContext.getSession().getJdbcServices(); + final var jdbcServices = executionContext.getSession().getJdbcServices(); this.jdbcParameterBindings = jdbcParameterBindings; this.executionContext = executionContext; @@ -71,7 +67,7 @@ public DeferredResultSetAccess( this.sqlStatementLogger = jdbcServices.getSqlStatementLogger(); this.resultCountEstimate = resultCountEstimate; - final QueryOptions queryOptions = executionContext.getQueryOptions(); + final var queryOptions = executionContext.getQueryOptions(); if ( queryOptions == null ) { finalSql = jdbcSelect.getSqlString(); limit = null; @@ -80,41 +76,54 @@ public DeferredResultSetAccess( else { // Note that limit and lock aren't set for SQM as that is applied during SQL rendering // But for native queries, we have to adapt the SQL string - final Dialect dialect = jdbcServices.getDialect(); + final var dialect = jdbcServices.getDialect(); final String sql = jdbcSelect.getSqlString(); limit = queryOptions.getLimit(); final boolean needsLimitHandler = needsLimitHandler( jdbcSelect ); limitHandler = needsLimitHandler ? dialect.getLimitHandler() : NoopLimitHandler.NO_LIMIT; - final String sqlWithLimit = !needsLimitHandler ? sql : limitHandler.processSql( - sql, - jdbcParameterBindings.getBindings().size(), - jdbcServices.getParameterMarkerStrategy(), - queryOptions - ); + final String sqlWithLimit = + needsLimitHandler + ? sqlWithLimit( jdbcParameterBindings, sql, jdbcServices, queryOptions ) + : sql; + + final String sqlWithLocking = + sqlWithLocking( jdbcSelect.getLockStrategy(), sqlWithLimit, queryOptions, dialect ); + + finalSql = + dialect.addSqlHintOrComment( sqlWithLocking, queryOptions, + executionContext.getSession().getFactory() + .getSessionFactoryOptions().isCommentsEnabled() ); + } + } - final var lockOptions = queryOptions.getLockOptions(); - final var jdbcLockStrategy = jdbcSelect.getLockStrategy(); - final String sqlWithLocking; - if ( hasLocking( jdbcLockStrategy, lockOptions ) ) { - final boolean usesFollowOnLocking = useFollowOnLocking( jdbcLockStrategy, sqlWithLimit, queryOptions, lockOptions, dialect ); - if ( usesFollowOnLocking ) { - sqlWithLocking = sqlWithLimit; - } - else { - sqlWithLocking = dialect.applyLocksToSql( sqlWithLimit, lockOptions, emptyMap() ); - } - } - else { - sqlWithLocking = sqlWithLimit; - } + private String sqlWithLimit( + JdbcParameterBindings jdbcParameterBindings, + String sql, + JdbcServices jdbcServices, + QueryOptions queryOptions) { + return limitHandler.processSql( + sql, + jdbcParameterBindings.getBindings().size(), + jdbcServices.getParameterMarkerStrategy(), + queryOptions + ); + } - final boolean commentsEnabled = executionContext.getSession() - .getFactory() - .getSessionFactoryOptions() - .isCommentsEnabled(); - finalSql = dialect.addSqlHintOrComment( sqlWithLocking, queryOptions, commentsEnabled ); + private static String sqlWithLocking( + JdbcLockStrategy jdbcLockStrategy, + String sqlWithLimit, + QueryOptions queryOptions, + Dialect dialect) { + final var lockOptions = queryOptions.getLockOptions(); + if ( hasLocking( jdbcLockStrategy, lockOptions ) ) { + return useFollowOnLocking( jdbcLockStrategy, sqlWithLimit, queryOptions, lockOptions, dialect ) + ? sqlWithLimit + : dialect.applyLocksToSql( sqlWithLimit, lockOptions, emptyMap() ); + } + else { + return sqlWithLimit; } } @@ -221,7 +230,7 @@ protected void bindParameters(PreparedStatement preparedStatement) throws SQLExc } private void setQueryOptions(PreparedStatement preparedStatement) throws SQLException { - final QueryOptions queryOptions = executionContext.getQueryOptions(); + final var queryOptions = executionContext.getQueryOptions(); // set options if ( queryOptions != null ) { final Integer fetchSize = queryOptions.getFetchSize(); @@ -238,10 +247,10 @@ private void setQueryOptions(PreparedStatement preparedStatement) throws SQLExce } private void executeQuery() { - final LogicalConnectionImplementor logicalConnection = + final var logicalConnection = getPersistenceContext().getJdbcCoordinator().getLogicalConnection(); - final SharedSessionContractImplementor session = executionContext.getSession(); + final var session = executionContext.getSession(); try { CORE_LOGGER.tracef( "Executing query to retrieve ResultSet: %s", finalSql ); // prepare the query @@ -254,8 +263,8 @@ private void executeQuery() { if ( sqlStatementLogger.getLogSlowQuery() > 0 ) { executeStartNanos = System.nanoTime(); } - final EventMonitor eventMonitor = session.getEventMonitor(); - final DiagnosticEvent jdbcPreparedStatementExecutionEvent = + final var eventMonitor = session.getEventMonitor(); + final var jdbcPreparedStatementExecutionEvent = eventMonitor.beginJdbcPreparedStatementExecutionEvent(); try { eventListenerManager.jdbcExecuteStatementStart(); @@ -326,8 +335,7 @@ protected LockMode determineFollowOnLockMode(LockOptions lockOptions) { @Override public void release() { - final JdbcCoordinator jdbcCoordinator = - getPersistenceContext().getJdbcCoordinator(); + final var jdbcCoordinator = getPersistenceContext().getJdbcCoordinator(); final LogicalConnectionImplementor logicalConnection = jdbcCoordinator.getLogicalConnection(); if ( resultSet != null ) { logicalConnection.getResourceRegistry().release( resultSet, preparedStatement ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DirectResultSetAccess.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DirectResultSetAccess.java index bc2a40a3f88a..8a589ff38565 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DirectResultSetAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DirectResultSetAccess.java @@ -25,7 +25,9 @@ public DirectResultSetAccess( this.resultSetSource = resultSetSource; this.resultSet = resultSet; - persistenceContext.getJdbcCoordinator().getLogicalConnection().getResourceRegistry() + persistenceContext.getJdbcCoordinator() + .getLogicalConnection() + .getResourceRegistry() .register( resultSet, resultSetSource ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesCacheHit.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesCacheHit.java index 45c7f0a037a8..200b050df28e 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesCacheHit.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesCacheHit.java @@ -36,47 +36,44 @@ public JdbcValuesCacheHit(List cachedResults, JdbcValuesMapping resolvedMappi @Override protected boolean processNext(RowProcessingState rowProcessingState) { - // NOTE : explicitly skipping limit handling because the cached state ought - // already be the limited size since the cache key includes limits - + // NOTE: explicitly skipping limit handling because the cached state ought + // already be the limited size since the cache key includes limits position++; - if ( position >= numberOfRows ) { position = numberOfRows; return false; } - - return true; + else { + return true; + } } @Override protected boolean processPrevious(RowProcessingState rowProcessingState) { - // NOTE : explicitly skipping limit handling because the cached state ought - // already be the limited size since the cache key includes limits - + // NOTE: explicitly skipping limit handling because the cached state ought + // already be the limited size since the cache key includes limits position--; - if ( position >= numberOfRows ) { position = numberOfRows; return false; } - - return true; + else { + return true; + } } @Override protected boolean processScroll(int numberOfRows, RowProcessingState rowProcessingState) { - // NOTE : explicitly skipping limit handling because the cached state should - // already be the limited size since the cache key includes limits - + // NOTE: explicitly skipping limit handling because the cached state should + // already be the limited size since the cache key includes limits position += numberOfRows; - if ( position >= this.numberOfRows ) { position = this.numberOfRows; return false; } - - return true; + else { + return true; + } } @Override @@ -86,8 +83,8 @@ public int getPosition() { @Override protected boolean processPosition(int position, RowProcessingState rowProcessingState) { - // NOTE : explicitly skipping limit handling because the cached state should - // already be the limited size since the cache key includes limits + // NOTE: explicitly skipping limit handling because the cached state should + // already be the limited size since the cache key includes limits if ( position < 0 ) { // we need to subtract it from `numberOfRows` @@ -102,9 +99,10 @@ protected boolean processPosition(int position, RowProcessingState rowProcessing this.position = numberOfRows; return false; } - - this.position = position; - return true; + else { + this.position = position; + return true; + } } @Override @@ -140,12 +138,9 @@ public void afterLast(RowProcessingState rowProcessingState) { @Override public boolean isLast(RowProcessingState rowProcessingState) { - if ( numberOfRows == 0 ) { - return position == 0; - } - else { - return position == numberOfRows - 1; - } + return numberOfRows == 0 + ? position == 0 + : position == numberOfRows - 1; } @Override @@ -154,9 +149,10 @@ public boolean last(RowProcessingState rowProcessingState) { position = 0; return false; } - - position = numberOfRows - 1; - return true; + else { + position = numberOfRows - 1; + return true; + } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesMappingProducerStandard.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesMappingProducerStandard.java index a7af8c6dc243..5d7256703b43 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesMappingProducerStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesMappingProducerStandard.java @@ -50,8 +50,8 @@ public JdbcValuesMapping resolve( final List sqlSelections = resolvedMapping.getSqlSelections(); List resolvedSelections = null; for ( int i = 0; i < sqlSelections.size(); i++ ) { - final SqlSelection sqlSelection = sqlSelections.get( i ); - final SqlSelection resolvedSelection = sqlSelection.resolve( jdbcResultsMetadata, sessionFactory ); + final var sqlSelection = sqlSelections.get( i ); + final var resolvedSelection = sqlSelection.resolve( jdbcResultsMetadata, sessionFactory ); if ( resolvedSelection != sqlSelection ) { if ( resolvedSelections == null ) { resolvedSelections = new ArrayList<>( sqlSelections ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesMappingResolutionImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesMappingResolutionImpl.java index fb54e6a4bdc6..2e7414d4ab93 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesMappingResolutionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesMappingResolutionImpl.java @@ -22,7 +22,10 @@ public JdbcValuesMappingResolutionImpl( DomainResultAssembler[] domainResultAssemblers, boolean hasCollectionInitializers, InitializersList initializersList) { - this( domainResultAssemblers, getResultInitializers( domainResultAssemblers ), hasCollectionInitializers, initializersList ); + this( domainResultAssemblers, + getResultInitializers( domainResultAssemblers ), + hasCollectionInitializers, + initializersList ); } private JdbcValuesMappingResolutionImpl( @@ -38,7 +41,7 @@ private JdbcValuesMappingResolutionImpl( private static Initializer[] getResultInitializers(DomainResultAssembler[] resultAssemblers) { final LinkedHashSet> initializers = new LinkedHashSet<>( resultAssemblers.length ); - for ( DomainResultAssembler resultAssembler : resultAssemblers ) { + for ( var resultAssembler : resultAssemblers ) { resultAssembler.forEachResultAssembler( (initializer, list) -> list.add( initializer ), initializers ); } return initializers.toArray(Initializer.EMPTY_ARRAY); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java index 0dd467d924e0..0a20cabb211e 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java @@ -9,11 +9,8 @@ import java.util.Arrays; import java.util.BitSet; -import org.hibernate.JDBCException; import org.hibernate.QueryTimeoutException; import org.hibernate.cache.spi.QueryKey; -import org.hibernate.cache.spi.QueryResultsCache; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.exception.DataException; import org.hibernate.exception.LockTimeoutException; @@ -27,6 +24,8 @@ import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; +import static java.util.Arrays.copyOf; + /** * {@link AbstractJdbcValues} implementation for a JDBC {@link ResultSet} as the source * @@ -77,7 +76,7 @@ public JdbcValuesResultSetImpl( final int rowSize = valuesMapping.getRowSize(); this.sqlSelections = new SqlSelection[rowSize]; - for ( SqlSelection selection : valuesMapping.getSqlSelections() ) { + for ( var selection : valuesMapping.getSqlSelections() ) { this.sqlSelections[selection.getValuesArrayPosition()] = selection; } this.initializedIndexes = new BitSet( rowSize ); @@ -127,11 +126,10 @@ private static QueryCachePutManager resolveQueryCachePutManager( String queryIdentifier, CachedJdbcValuesMetadata metadataForCache) { if ( queryCacheKey != null ) { - final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); - final QueryResultsCache queryCache = factory.getCache() - .getQueryResultsCache( queryOptions.getResultCacheRegionName() ); + final var factory = executionContext.getSession().getFactory(); return new QueryCachePutManagerEnabledImpl( - queryCache, + factory.getCache() + .getQueryResultsCache( queryOptions.getResultCacheRegionName() ), factory.getStatistics(), queryCacheKey, queryIdentifier, @@ -300,18 +298,16 @@ private boolean advancePrevious() { } private boolean advance(final boolean hasResult) { - if ( ! hasResult ) { - return false; + if ( hasResult ) { + readCurrentRowValues(); } - - readCurrentRowValues(); - return true; + return hasResult; } private ExecutionException makeExecutionException(String message, SQLException cause) { - final JDBCException jdbcException = - executionContext.getSession().getJdbcServices().getSqlExceptionHelper() - .convert( cause, message ); + final var jdbcException = + executionContext.getSession().getJdbcServices() + .getSqlExceptionHelper().convert( cause, message ); if ( jdbcException instanceof QueryTimeoutException || jdbcException instanceof DataException || jdbcException instanceof LockTimeoutException ) { @@ -352,7 +348,7 @@ public void finishRowProcessing(RowProcessingState rowProcessingState, boolean w } final Object objectToCache; if ( valueIndexesToCacheIndexes == null ) { - objectToCache = Arrays.copyOf( currentRowJdbcValues, currentRowJdbcValues.length ); + objectToCache = copyOf( currentRowJdbcValues, currentRowJdbcValues.length ); } else if ( rowToCacheSize < 1 ) { if ( !wasAdded ) { @@ -362,7 +358,7 @@ else if ( rowToCacheSize < 1 ) { objectToCache = currentRowJdbcValues[-rowToCacheSize]; } else { - final Object[] rowToCache = new Object[rowToCacheSize]; + final var rowToCache = new Object[rowToCacheSize]; for ( int i = 0; i < currentRowJdbcValues.length; i++ ) { final int cacheIndex = valueIndexesToCacheIndexes[i]; if ( cacheIndex != -1 ) { @@ -379,7 +375,7 @@ else if ( rowToCacheSize < 1 ) { public Object getCurrentRowValue(int valueIndex) { if ( !initializedIndexes.get( valueIndex ) ) { initializedIndexes.set( valueIndex ); - final SqlSelection sqlSelection = sqlSelections[valueIndex]; + final var sqlSelection = sqlSelections[valueIndex]; final int index = sqlSelection.getJdbcResultSetIndex(); try { currentRowJdbcValues[valueIndex] = sqlSelection.getJdbcValueExtractor().extract( diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java index a45c3200e46f..0b77d5c813db 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java @@ -12,7 +12,6 @@ import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.EntityHolder; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.PostLoadEvent; import org.hibernate.event.spi.PreLoadEvent; import org.hibernate.query.spi.QueryOptions; @@ -46,7 +45,7 @@ public JdbcValuesSourceProcessingStateStandardImpl( this.processingOptions = processingOptions; if ( executionContext.getSession().isEventSource() ) { - final EventSource eventSource = executionContext.getSession().asEventSource(); + final var eventSource = executionContext.getSession().asEventSource(); preLoadEvent = new PreLoadEvent( eventSource ); postLoadEvent = new PostLoadEvent( eventSource ); } @@ -151,33 +150,19 @@ public SharedSessionContractImplementor getSession() { public void finishUp(boolean registerSubselects) { // now we can finalize loading collections finishLoadingCollections(); - getSession().getPersistenceContextInternal() .postLoad( this, registerSubselects ? executionContext::registerLoadingEntityHolder : null ); } - private boolean isReadOnly() { - if ( getQueryOptions().isReadOnly() != null ) { - return getQueryOptions().isReadOnly(); - } - else if ( getSession() instanceof EventSource ) { - return getSession().isDefaultReadOnly(); - } - else { - return false; - } - } - /** * For Hibernate Reactive */ public void finishLoadingCollections() { if ( loadingCollectionMap != null ) { - for ( LoadingCollectionEntry loadingCollectionEntry : loadingCollectionMap.values() ) { + for ( var loadingCollectionEntry : loadingCollectionMap.values() ) { loadingCollectionEntry.finishLoading( getExecutionContext() ); } - loadingCollectionMap = null; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/StandardJdbcValuesMapping.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/StandardJdbcValuesMapping.java index e83c28974af9..5e0736432f70 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/StandardJdbcValuesMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/StandardJdbcValuesMapping.java @@ -6,6 +6,7 @@ import java.util.BitSet; import java.util.List; +import java.util.Objects; import java.util.function.Supplier; import org.hibernate.LockMode; @@ -55,15 +56,15 @@ public StandardJdbcValuesMapping( this.domainResults = domainResults; final int rowSize = sqlSelections.size(); - final BitSet valueIndexesToCache = new BitSet( rowSize ); - for ( DomainResult domainResult : domainResults ) { + final var valueIndexesToCache = new BitSet( rowSize ); + for ( var domainResult : domainResults ) { domainResult.collectValueIndexesToCache( valueIndexesToCache ); } final int[] valueIndexesToCacheIndexes = new int[rowSize]; int cacheIndex = 0; boolean needsResolve = false; for ( int i = 0; i < valueIndexesToCacheIndexes.length; i++ ) { - final SqlSelection sqlSelection = sqlSelections.get( i ); + final var sqlSelection = sqlSelections.get( i ); needsResolve = needsResolve || sqlSelection instanceof SqlSelectionImpl selection && selection.needsResolve(); if ( valueIndexesToCache.get( i ) ) { @@ -114,7 +115,7 @@ public JdbcValuesMappingResolution resolveAssemblers(SessionFactoryImplementor s return resolution; } else { - final AssemblerCreationStateImpl creationState = + final var creationState = new AssemblerCreationStateImpl( this, sessionFactory.getSqlTranslationEngine() ); final var domainResultAssemblers = resolveAssemblers( creationState ); @@ -132,7 +133,7 @@ private DomainResultAssembler[] resolveAssemblers(AssemblerCreationState crea final int size = domainResults.size(); final List> assemblers = arrayList( size ); for ( int i = 0; i < size; i++ ) { - final DomainResultAssembler resultAssembler = + final var resultAssembler = domainResults.get( i ) .createResultAssembler( null, creationState ); assemblers.add( resultAssembler ); @@ -166,9 +167,9 @@ public AssemblerCreationStateImpl( @Override public boolean isDynamicInstantiation() { if ( dynamicInstantiation == null ) { - dynamicInstantiation = jdbcValuesMapping.getDomainResults() - .stream() - .anyMatch( domainResult -> domainResult instanceof DynamicInstantiationResult ); + dynamicInstantiation = + jdbcValuesMapping.getDomainResults().stream() + .anyMatch( domainResult -> domainResult instanceof DynamicInstantiationResult ); } return dynamicInstantiation; } @@ -177,7 +178,7 @@ public boolean isDynamicInstantiation() { public boolean containsMultipleCollectionFetches() { if ( containsMultipleCollectionFetches == null ) { int collectionFetchesCount = 0; - for ( DomainResult domainResult : jdbcValuesMapping.getDomainResults() ) { + for ( var domainResult : jdbcValuesMapping.getDomainResults() ) { if ( domainResult instanceof FetchParent fetchParent ) { collectionFetchesCount += fetchParent.getCollectionFetchesCount(); } @@ -226,16 +227,15 @@ public Initializer resolveInitializer( T resultGraphNode, InitializerParent parent, InitializerProducer producer) { - final Initializer existing = initializerMap.get( navigablePath ); - if ( existing != null ) { - if ( fetchedModelPart.getNavigableRole().equals( + final var existing = initializerMap.get( navigablePath ); + if ( existing != null + && Objects.equals( fetchedModelPart.getNavigableRole(), existing.getInitializedPart().getNavigableRole() ) ) { - RESULTS_MESSAGE_LOGGER.tracef( "Returning previously-registered initializer: %s", existing ); - return existing; - } + RESULTS_MESSAGE_LOGGER.tracef( "Returning previously-registered initializer: %s", existing ); + return existing; } - final Initializer initializer = producer.createInitializer( resultGraphNode, parent, this ); + final var initializer = producer.createInitializer( resultGraphNode, parent, this ); RESULTS_MESSAGE_LOGGER.tracef( "Registering initializer: %s", initializer ); if ( initializer instanceof AbstractImmediateCollectionInitializer ) { From 4330fb7bc76b5412695cfb5f1bc2f9b34b100b67 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 5 Dec 2025 21:15:22 +0100 Subject: [PATCH 031/181] eliminate some unnecessary overrides in JavaTypes --- .../type/descriptor/java/BasicJavaType.java | 3 ++- .../descriptor/java/CalendarDateJavaType.java | 2 +- .../type/descriptor/java/CalendarJavaType.java | 2 +- .../descriptor/java/CalendarTimeJavaType.java | 2 +- .../java/spi/EmbeddableAggregateJavaType.java | 12 ------------ .../type/descriptor/java/spi/EntityJavaType.java | 15 ++------------- .../descriptor/java/spi/JavaTypeBasicAdaptor.java | 12 ------------ .../descriptor/java/spi/MapEntryJavaType.java | 5 ----- .../descriptor/java/spi/UnknownBasicJavaType.java | 12 ------------ 9 files changed, 7 insertions(+), 58 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BasicJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BasicJavaType.java index 77eb3f485218..58032644ecda 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BasicJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BasicJavaType.java @@ -34,6 +34,7 @@ default JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { @Override default T fromString(CharSequence string) { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException( "Type " + getTypeName() + + " does not support conversion from String"); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java index db5446e701ca..26d916fed2c0 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java @@ -60,7 +60,7 @@ public String toString(Calendar value) { } public Calendar fromString(CharSequence string) { - Calendar result = new GregorianCalendar(); + final var result = new GregorianCalendar(); result.setTime( JdbcDateJavaType.INSTANCE.fromString( string.toString() ) ); return result; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java index 3b23b21b162f..517566d06959 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java @@ -69,7 +69,7 @@ public String toString(Calendar value) { } public Calendar fromString(CharSequence string) { - Calendar result = new GregorianCalendar(); + final var result = new GregorianCalendar(); result.setTime( DateJavaType.INSTANCE.fromString( string.toString() ) ); return result; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java index 077c1fdb50c9..bd6a6e91fc30 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java @@ -60,7 +60,7 @@ public String toString(Calendar value) { } public Calendar fromString(CharSequence string) { - Calendar result = new GregorianCalendar(); + final var result = new GregorianCalendar(); result.setTime( JdbcTimeJavaType.INSTANCE.fromString( string.toString() ) ); return result; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EmbeddableAggregateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EmbeddableAggregateJavaType.java index b5d5613c9fcb..4e9d0d189978 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EmbeddableAggregateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EmbeddableAggregateJavaType.java @@ -67,18 +67,6 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { ); } - @Override - public String toString(T value) { - return value.toString(); - } - - @Override - public T fromString(CharSequence string) { - throw new UnsupportedOperationException( - "Conversion from String strategy not known for this Java type: " + getTypeName() - ); - } - @Override public X unwrap(T value, Class type, WrapperOptions options) { if ( type.isAssignableFrom( getJavaTypeClass() ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java index a4304068fafe..fa57add4632d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java @@ -11,6 +11,7 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import static java.lang.System.identityHashCode; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; /** @@ -36,7 +37,7 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { @Override public int extractHashCode(T value) { - return System.identityHashCode( value ); + return identityHashCode( value ); } @Override @@ -57,18 +58,6 @@ public boolean isInstance(Object value) { } } - @Override - public String toString(T value) { - return value.toString(); - } - - @Override - public T fromString(CharSequence string) { - throw new UnsupportedOperationException( - "Conversion from String strategy not known for this Java type: " + getTypeName() - ); - } - @Override @SuppressWarnings("unchecked") // safe, we do check public X unwrap(T value, Class type, WrapperOptions options) { final var id = diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeBasicAdaptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeBasicAdaptor.java index 44e1bf3fa45f..fe24516b513e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeBasicAdaptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeBasicAdaptor.java @@ -37,18 +37,6 @@ public boolean useObjectEqualsHashCode() { return true; } - @Override - public String toString(T value) { - return value.toString(); - } - - @Override - public T fromString(CharSequence string) { - throw new UnsupportedOperationException( - "Conversion from String strategy not known for this Java type: " + getTypeName() - ); - } - @Override public X unwrap(T value, Class type, WrapperOptions options) { throw new UnsupportedOperationException( diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/MapEntryJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/MapEntryJavaType.java index 833cb7cb65b9..0690edf93de8 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/MapEntryJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/MapEntryJavaType.java @@ -31,11 +31,6 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { throw new UnsupportedOperationException( "Unsupported attempt to resolve JDBC type for Map.Entry" ); } - @Override - public Map.Entry fromString(CharSequence string) { - throw new UnsupportedOperationException( "Unsupported attempt create Map.Entry from String" ); - } - @Override public X unwrap(Map.Entry value, Class type, WrapperOptions options) { throw new UnsupportedOperationException( "Unsupported attempt to unwrap Map.Entry value" ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/UnknownBasicJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/UnknownBasicJavaType.java index 96ec9049b3b7..cd8350352248 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/UnknownBasicJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/UnknownBasicJavaType.java @@ -61,18 +61,6 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { ); } - @Override - public String toString(T value) { - return value.toString(); - } - - @Override - public T fromString(CharSequence string) { - throw new UnsupportedOperationException( - "Conversion from String strategy not known for this Java type: " + getTypeName() - ); - } - @Override public X unwrap(T value, Class type, WrapperOptions options) { if ( type.isAssignableFrom( getJavaTypeClass() ) ) { From e991a422a6e1eafc9588b921dbb09e750bc85c57 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 5 Dec 2025 23:29:36 +0100 Subject: [PATCH 032/181] fix to code generation for Jakarta Data delete() method --- .../processor/annotation/CriteriaDeleteMethod.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/CriteriaDeleteMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/CriteriaDeleteMethod.java index 58d88fc997b7..49fe22213e39 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/CriteriaDeleteMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/CriteriaDeleteMethod.java @@ -86,11 +86,11 @@ String specificationType() { private void execute(StringBuilder declaration) { declaration - .append("\t\t\t.executeUpdate()"); - if ( isReactive() ) { - if ( fullReturnType.endsWith("") ) {} + .append(".executeUpdate()"); + if ( isReactive() + && fullReturnType.endsWith("") ) { declaration - .append(".replaceWithVoid()"); + .append( ".replaceWithVoid()" ); } } From ee2fa41811d931c2d08afb134723d745deac9a51 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 6 Dec 2025 08:37:37 +0100 Subject: [PATCH 033/181] minor aesthetic cleanups to the date/time JavaTypes --- .../descriptor/java/JdbcDateJavaType.java | 40 ++++---- .../descriptor/java/JdbcTimeJavaType.java | 29 +++--- .../java/JdbcTimestampJavaType.java | 12 +-- .../descriptor/java/LocalDateJavaType.java | 33 +++---- .../java/LocalDateTimeJavaType.java | 24 ++--- .../descriptor/java/LocalTimeJavaType.java | 24 ++--- .../java/OffsetDateTimeJavaType.java | 79 ++++++++-------- .../descriptor/java/OffsetTimeJavaType.java | 92 +++++++++---------- .../java/ZonedDateTimeJavaType.java | 61 ++++++------ 9 files changed, 181 insertions(+), 213 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java index a60835ae643b..17d55e90340e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java @@ -6,11 +6,11 @@ import java.sql.Types; import java.time.LocalDate; -import java.time.LocalTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -114,7 +114,7 @@ public Date coerce(Object value, CoercionContext coercionContext) { return wrap( value, null ); } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) @Override public Object unwrap(Date value, Class type, WrapperOptions options) { if ( value == null ) { @@ -169,8 +169,9 @@ private java.sql.Date unwrapSqlDate(Date value) { final long dateEpoch = toDateEpoch( date.getTime() ); return dateEpoch == date.getTime() ? date : new java.sql.Date( dateEpoch ); } - return new java.sql.Date( unwrapDateEpoch( value ) ); - + else { + return new java.sql.Date( unwrapDateEpoch( value ) ); + } } private static long unwrapDateEpoch(Date value) { @@ -178,7 +179,7 @@ private static long unwrapDateEpoch(Date value) { } private static long toDateEpoch(long value) { - Calendar calendar = Calendar.getInstance(); + final var calendar = Calendar.getInstance(); calendar.setTimeInMillis( value ); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.clear(Calendar.MINUTE); @@ -216,14 +217,15 @@ public Date wrap(Object value, WrapperOptions options) { throw unknownWrap( value.getClass() ); } + private static TemporalAccessor fromDate(Date value) { + return value instanceof java.sql.Date date + ? date.toLocalDate() + : LocalDate.ofInstant( value.toInstant(), ZoneOffset.systemDefault() ); + } + @Override public String toString(Date value) { - if ( value instanceof java.sql.Date ) { - return LITERAL_FORMATTER.format( ( (java.sql.Date) value ).toLocalDate() ); - } - else { - return LITERAL_FORMATTER.format( LocalDate.ofInstant( value.toInstant(), ZoneOffset.systemDefault() ) ); - } + return LITERAL_FORMATTER.format( fromDate( value ) ); } @Override @@ -250,12 +252,7 @@ public Date fromEncodedString(CharSequence charSequence, int start, int end) { @Override public void appendEncodedString(SqlAppender sb, Date value) { - if ( value instanceof java.sql.Date ) { - LITERAL_FORMATTER.formatTo( ( (java.sql.Date) value ).toLocalDate(), sb ); - } - else { - LITERAL_FORMATTER.formatTo( LocalTime.ofInstant( value.toInstant(), ZoneOffset.systemDefault() ), sb ); - } + LITERAL_FORMATTER.formatTo( fromDate( value ), sb ); } @Override @@ -274,12 +271,9 @@ public static class DateMutabilityPlan extends MutableMutabilityPlan { @Override public Date deepCopyNotNull(Date value) { - if ( value instanceof java.sql.Date ) { - return value; - } - else { - return new java.sql.Date( value.getTime() ); - } + return value instanceof java.sql.Date + ? value + : new java.sql.Date( value.getTime() ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java index 0ae67b8a4367..f5dc8d21ea55 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java @@ -116,7 +116,7 @@ public Date coerce(Object value, CoercionContext coercionContext) { return wrap( value, null ); } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) @Override public Object unwrap(Date value, Class type, WrapperOptions options) { if ( value == null ) { @@ -124,9 +124,10 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { } if ( LocalTime.class.isAssignableFrom( type ) ) { - final Time time = value instanceof java.sql.Time - ? (java.sql.Time) value - : new java.sql.Time( value.getTime() % 86_400_000 ); + final var time = + value instanceof java.sql.Time + ? (java.sql.Time) value + : new java.sql.Time( value.getTime() % 86_400_000 ); final var localTime = time.toLocalTime(); long millis = time.getTime() % 1000; if ( millis == 0 ) { @@ -209,14 +210,15 @@ public Date wrap(Object value, WrapperOptions options) { throw unknownWrap( value.getClass() ); } + private static LocalTime fromDate(Date value) { + return value instanceof Time time + ? time.toLocalTime() + : LocalTime.ofInstant( value.toInstant(), ZoneOffset.systemDefault() ); + } + @Override public String toString(Date value) { - if ( value instanceof java.sql.Time time ) { - return LITERAL_FORMATTER.format( time.toLocalTime() ); - } - else { - return LITERAL_FORMATTER.format( LocalTime.ofInstant( value.toInstant(), ZoneOffset.systemDefault() ) ); - } + return LITERAL_FORMATTER.format( fromDate( value ) ); } @Override @@ -246,12 +248,7 @@ public Date fromEncodedString(CharSequence charSequence, int start, int end) { @Override public void appendEncodedString(SqlAppender sb, Date value) { - if ( value instanceof java.sql.Time time ) { - LITERAL_FORMATTER.formatTo( time.toLocalTime(), sb ); - } - else { - LITERAL_FORMATTER.formatTo( LocalTime.ofInstant( value.toInstant(), ZoneOffset.systemDefault() ), sb ); - } + LITERAL_FORMATTER.formatTo( fromDate( value ), sb ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java index 601b91699397..e18fbc917e1e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java @@ -6,7 +6,6 @@ import java.sql.Timestamp; import java.sql.Types; -import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZoneOffset; @@ -120,7 +119,7 @@ public Date coerce(Object value, CoercionContext coercionContext) { return wrap( value, null ); } - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) @Override public Object unwrap(Date value, Class type, WrapperOptions options) { if ( value == null ) { @@ -138,7 +137,7 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { } if ( LocalDateTime.class.isAssignableFrom( type ) ) { - final Instant instant = value.toInstant(); + final var instant = value.toInstant(); return LocalDateTime.ofInstant( instant, ZoneId.systemDefault() ); } @@ -230,15 +229,14 @@ public void appendEncodedString(SqlAppender sb, Date value) { public Date fromEncodedString(CharSequence charSequence, int start, int end) { try { final var temporalAccessor = ENCODED_FORMATTER.parse( subSequence( charSequence, start, end ) ); - final Timestamp timestamp; if ( temporalAccessor.isSupported( ChronoField.INSTANT_SECONDS ) ) { - timestamp = new Timestamp( temporalAccessor.getLong( ChronoField.INSTANT_SECONDS ) * 1000L ); + final var timestamp = new Timestamp( temporalAccessor.getLong( ChronoField.INSTANT_SECONDS ) * 1000L ); timestamp.setNanos( temporalAccessor.get( ChronoField.NANO_OF_SECOND ) ); + return timestamp; } else { - timestamp = Timestamp.valueOf( LocalDateTime.from( temporalAccessor ) ); + return Timestamp.valueOf( LocalDateTime.from( temporalAccessor ) ); } - return timestamp; } catch ( DateTimeParseException pe) { throw new HibernateException( "could not parse timestamp string " + subSequence( charSequence, start, end ), pe ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java index 93658de28f30..949d2e4d940d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java @@ -94,13 +94,11 @@ public X unwrap(LocalDate value, Class type, WrapperOptions options) { final LocalDateTime localDateTime = value.atStartOfDay(); if ( Timestamp.class.isAssignableFrom( type ) ) { - /* - * Workaround for HHH-13266 (JDK-8061577). - * We could have done Timestamp.from( localDateTime.atZone( ZoneId.systemDefault() ).toInstant() ), - * but on top of being more complex than the line below, it won't always work. - * Timestamp.from() assumes the number of milliseconds since the epoch - * means the same thing in Timestamp and Instant, but it doesn't, in particular before 1900. - */ + // Workaround for HHH-13266 (JDK-8061577). + // We could have done Timestamp.from( localDateTime.atZone( ZoneId.systemDefault() ).toInstant() ), + // but on top of being more complex than the line below, it won't always work. + // Timestamp.from() assumes the number of milliseconds since the epoch means the + // same thing in Timestamp and Instant, but it doesn't, in particular before 1900. return (X) Timestamp.valueOf( localDateTime ); } @@ -134,13 +132,11 @@ public LocalDate wrap(X value, WrapperOptions options) { } if (value instanceof Timestamp timestamp) { - /* - * Workaround for HHH-13266 (JDK-8061577). - * We used to do LocalDateTime.ofInstant( ts.toInstant(), ZoneId.systemDefault() ).toLocalDate(), - * but on top of being more complex than the line below, it won't always work. - * ts.toInstant() assumes the number of milliseconds since the epoch - * means the same thing in Timestamp and Instant, but it doesn't, in particular before 1900. - */ + // Workaround for HHH-13266 (JDK-8061577). + // We used to do LocalDateTime.ofInstant( ts.toInstant(), ZoneId.systemDefault() ).toLocalDate(), + // but on top of being more complex than the line below, it won't always work. + // ts.toInstant() assumes the number of milliseconds since the epoch means the + // same thing in Timestamp and Instant, but it doesn't, in particular before 1900. return timestamp.toLocalDateTime().toLocalDate(); } @@ -154,12 +150,9 @@ public LocalDate wrap(X value, WrapperOptions options) { } if (value instanceof Date date) { - if (value instanceof java.sql.Date sqlDate) { - return sqlDate.toLocalDate(); - } - else { - return Instant.ofEpochMilli( date.getTime() ).atZone( ZoneId.systemDefault() ).toLocalDate(); - } + return value instanceof java.sql.Date sqlDate + ? sqlDate.toLocalDate() + : Instant.ofEpochMilli( date.getTime() ).atZone( ZoneId.systemDefault() ).toLocalDate(); } throw unknownWrap( value.getClass() ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java index 8a4d64752a54..426903e455c3 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java @@ -89,13 +89,11 @@ public X unwrap(LocalDateTime value, Class type, WrapperOptions options) } if ( Timestamp.class.isAssignableFrom( type ) ) { - /* - * Workaround for HHH-13266 (JDK-8061577). - * We used to do Timestamp.from( value.atZone( ZoneId.systemDefault() ).toInstant() ), - * but on top of being more complex than the line below, it won't always work. - * Timestamp.from() assumes the number of milliseconds since the epoch - * means the same thing in Timestamp and Instant, but it doesn't, in particular before 1900. - */ + // Workaround for HHH-13266 (JDK-8061577). + // We used to do Timestamp.from( value.atZone( ZoneId.systemDefault() ).toInstant() ), + // but on top of being more complex than the line below, it won't always work. + // Timestamp.from() assumes the number of milliseconds since the epoch means the + // same thing in Timestamp and Instant, but it doesn't, in particular before 1900. return (X) Timestamp.valueOf( value ); } @@ -137,13 +135,11 @@ public LocalDateTime wrap(X value, WrapperOptions options) { } if (value instanceof Timestamp timestamp) { - /* - * Workaround for HHH-13266 (JDK-8061577). - * We used to do LocalDateTime.ofInstant( ts.toInstant(), ZoneId.systemDefault() ), - * but on top of being more complex than the line below, it won't always work. - * ts.toInstant() assumes the number of milliseconds since the epoch - * means the same thing in Timestamp and Instant, but it doesn't, in particular before 1900. - */ + // Workaround for HHH-13266 (JDK-8061577). + // We used to do LocalDateTime.ofInstant( ts.toInstant(), ZoneId.systemDefault() ), + // but on top of being more complex than the line below, it won't always work. + // ts.toInstant() assumes the number of milliseconds since the epoch means the + // same thing in Timestamp and Instant, but it doesn't, in particular before 1900. return timestamp.toLocalDateTime(); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java index e7d981fbd723..ac7a498cba6d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java @@ -22,12 +22,13 @@ import org.hibernate.dialect.Dialect; import org.hibernate.type.SqlTypes; -import org.hibernate.type.descriptor.DateTimeUtils; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; import org.hibernate.type.spi.TypeConfiguration; +import static org.hibernate.type.descriptor.DateTimeUtils.roundToPrecision; + /** * Java type descriptor for the {@link LocalTime} type. * @@ -94,19 +95,20 @@ public X unwrap(LocalTime value, Class type, WrapperOptions options) { if ( Time.class.isAssignableFrom( type ) ) { final var time = Time.valueOf( value ); - if ( value.getNano() == 0 ) { - return (X) time; - } - // Preserve milliseconds, which java.sql.Time supports - return (X) new Time( time.getTime() + DateTimeUtils.roundToPrecision( value.getNano(), 3 ) / 1000000 ); + final int nanos = value.getNano(); + return nanos == 0 + ? (X) time + // Preserve milliseconds, which java.sql.Time supports + : (X) new Time( time.getTime() + roundToPrecision( nanos, 3 ) / 1000000 ); } // Oracle documentation says to set the Date to January 1, 1970 when convert from - // a LocalTime to a Calendar. IMO the same should hold true for converting to all - // the legacy Date/Time types... - + // a LocalTime to a Calendar. IMO the same should hold true for converting to all + // the legacy Date/Time types. - final var zonedDateTime = value.atDate( LocalDate.of( 1970, 1, 1 ) ).atZone( ZoneId.systemDefault() ); + final var zonedDateTime = + value.atDate( LocalDate.of( 1970, 1, 1 ) ) + .atZone( ZoneId.systemDefault() ); if ( Calendar.class.isAssignableFrom( type ) ) { return (X) GregorianCalendar.from( zonedDateTime ); @@ -140,7 +142,7 @@ public LocalTime wrap(X value, WrapperOptions options) { } if (value instanceof Time time) { - final LocalTime localTime = time.toLocalTime(); + final var localTime = time.toLocalTime(); long millis = time.getTime() % 1000; if ( millis == 0 ) { return localTime; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java index 8e8b36cc1d29..7cbdb112578a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java @@ -15,7 +15,6 @@ import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; -import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -75,10 +74,9 @@ public TemporalType getPrecision() { @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators stdIndicators) { - if ( stdIndicators.isPreferJavaTimeJdbcTypesEnabled() ) { - return stdIndicators.getJdbcType( SqlTypes.OFFSET_DATE_TIME ); - } - return stdIndicators.getJdbcType( stdIndicators.getDefaultZonedTimestampSqlType() ); + return stdIndicators.isPreferJavaTimeJdbcTypesEnabled() + ? stdIndicators.getJdbcType( SqlTypes.OFFSET_DATE_TIME ) + : stdIndicators.getJdbcType( stdIndicators.getDefaultZonedTimestampSqlType() ); } @Override @SuppressWarnings("unchecked") @@ -104,17 +102,16 @@ public OffsetDateTime fromString(CharSequence string) { @Override public OffsetDateTime fromEncodedString(CharSequence charSequence, int start, int end) { try { - final TemporalAccessor temporalAccessor = PARSE_FORMATTER.parse( subSequence( charSequence, start, end ) ); - if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { - return OffsetDateTime.from( temporalAccessor ); - } - else { - // For databases that don't have timezone support, we encode timestamps at UTC, so allow parsing that as well - return LocalDateTime.from( temporalAccessor ).atOffset( ZoneOffset.UTC ); - } + final var temporalAccessor = PARSE_FORMATTER.parse( subSequence( charSequence, start, end ) ); + return temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) + ? OffsetDateTime.from( temporalAccessor ) + // For databases that don't have timezone support, + // we encode timestamps at UTC, so allow parsing + : LocalDateTime.from( temporalAccessor ).atOffset( ZoneOffset.UTC ); } catch ( DateTimeParseException pe) { - throw new HibernateException( "could not parse timestamp string " + subSequence( charSequence, start, end ), pe ); + throw new HibernateException( "could not parse timestamp string " + + subSequence( charSequence, start, end ), pe ); } } @@ -142,19 +139,21 @@ public X unwrap(OffsetDateTime offsetDateTime, Class type, WrapperOptions } if ( Timestamp.class.isAssignableFrom( type ) ) { - /* - * This works around two bugs: - * - HHH-13266 (JDK-8061577): around and before 1900, - * the number of milliseconds since the epoch does not mean the same thing - * for java.util and java.time, so conversion must be done using the year, month, day, hour, etc. - * - HHH-13379 (JDK-4312621): after 1908 (approximately), - * Daylight Saving Time introduces ambiguity in the year/month/day/hour/etc representation once a year - * (on DST end), so conversion must be done using the number of milliseconds since the epoch. - * - around 1905, both methods are equally valid, so we don't really care which one is used. - */ + // This works around two bugs: + // - HHH-13266 (JDK-8061577): around and before 1900, + // the number of milliseconds since the epoch does not mean the same thing + // for java.util and java.time, so conversion must be done using the year, + // month, day, hour, etc. + // - HHH-13379 (JDK-4312621): after 1908 (approximately), + // Daylight Saving Time introduces ambiguity in the year/month/day/hour/etc + // representation once a year (on DST end), so conversion must be done using + // the number of milliseconds since the epoch. + // - around 1905, both methods are equally valid, so we don't really care which + // one is used. if ( offsetDateTime.getYear() < 1905 ) { return (X) Timestamp.valueOf( - offsetDateTime.atZoneSameInstant( ZoneId.systemDefault() ).toLocalDateTime() + offsetDateTime.atZoneSameInstant( ZoneId.systemDefault() ) + .toLocalDateTime() ); } else { @@ -200,22 +199,20 @@ public OffsetDateTime wrap(X value, WrapperOptions options) { } if (value instanceof Timestamp timestamp) { - /* - * This works around two bugs: - * - HHH-13266 (JDK-8061577): around and before 1900, - * the number of milliseconds since the epoch does not mean the same thing - * for java.util and java.time, so conversion must be done using the year, month, day, hour, etc. - * - HHH-13379 (JDK-4312621): after 1908 (approximately), - * Daylight Saving Time introduces ambiguity in the year/month/day/hour/etc representation once a year - * (on DST end), so conversion must be done using the number of milliseconds since the epoch. - * - around 1905, both methods are equally valid, so we don't really care which one is used. - */ - if ( timestamp.getYear() < 5 ) { // Timestamp year 0 is 1900 - return timestamp.toLocalDateTime().atZone( ZoneId.systemDefault() ).toOffsetDateTime(); - } - else { - return OffsetDateTime.ofInstant( timestamp.toInstant(), ZoneId.systemDefault() ); - } + // This works around two bugs: + // - HHH-13266 (JDK-8061577): around and before 1900, + // the number of milliseconds since the epoch does not mean the same thing + // for java.util and java.time, so conversion must be done using the year, + // month, day, hour, etc. + // - HHH-13379 (JDK-4312621): after 1908 (approximately), + // Daylight Saving Time introduces ambiguity in the year/month/day/hour/etc + // representation once a year (on DST end), so conversion must be done using + // the number of milliseconds since the epoch. + // - around 1905, both methods are equally valid, so we don't really care which + // one is used. + return timestamp.getYear() < 5 // Timestamp year 0 is 1900 + ? timestamp.toLocalDateTime().atZone( ZoneId.systemDefault() ).toOffsetDateTime() + : OffsetDateTime.ofInstant( timestamp.toInstant(), ZoneId.systemDefault() ); } if (value instanceof Date date) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java index 7d32af5f59ab..0c4ec1a3c9dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java @@ -20,7 +20,6 @@ import org.hibernate.dialect.Dialect; import org.hibernate.type.SqlTypes; -import org.hibernate.type.descriptor.DateTimeUtils; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; @@ -28,6 +27,8 @@ import jakarta.persistence.TemporalType; +import static org.hibernate.type.descriptor.DateTimeUtils.roundToPrecision; + /** * Java type descriptor for the {@link OffsetTime} type. * @@ -55,10 +56,9 @@ public TemporalType getPrecision() { @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators stdIndicators) { - if ( stdIndicators.isPreferJavaTimeJdbcTypesEnabled() ) { - return stdIndicators.getJdbcType( SqlTypes.OFFSET_TIME ); - } - return stdIndicators.getJdbcType( stdIndicators.getDefaultZonedTimeSqlType() ); + return stdIndicators.isPreferJavaTimeJdbcTypesEnabled() + ? stdIndicators.getJdbcType( SqlTypes.OFFSET_TIME ) + : stdIndicators.getJdbcType( stdIndicators.getDefaultZonedTimeSqlType() ); } @Override @@ -107,27 +107,25 @@ public X unwrap(OffsetTime offsetTime, Class type, WrapperOptions options // for legacy types, we assume that the JDBC timezone is passed to JDBC // (since PS.setTime() and friends do accept a timezone passed as a Calendar) - final OffsetTime jdbcOffsetTime = offsetTime.withOffsetSameInstant( getCurrentJdbcOffset(options) ); + final var jdbcOffsetTime = offsetTime.withOffsetSameInstant( getCurrentJdbcOffset(options) ); if ( Time.class.isAssignableFrom( type ) ) { - final Time time = Time.valueOf( jdbcOffsetTime.toLocalTime() ); - if ( jdbcOffsetTime.getNano() == 0 ) { - return (X) time; - } - // Preserve milliseconds, which java.sql.Time supports - return (X) new Time( time.getTime() + DateTimeUtils.roundToPrecision( jdbcOffsetTime.getNano(), 3 ) / 1000000 ); + final var time = Time.valueOf( jdbcOffsetTime.toLocalTime() ); + final int nanos = jdbcOffsetTime.getNano(); + return nanos == 0 + ? (X) time + // Preserve milliseconds, which java.sql.Time supports + : (X) new Time( time.getTime() + roundToPrecision( nanos, 3 ) / 1000000 ); } - final OffsetDateTime jdbcOffsetDateTime = jdbcOffsetTime.atDate( LocalDate.EPOCH ); + final var jdbcOffsetDateTime = jdbcOffsetTime.atDate( LocalDate.EPOCH ); if ( Timestamp.class.isAssignableFrom( type ) ) { - /* - * Workaround for HHH-13266 (JDK-8061577). - * Ideally we'd want to use Timestamp.from( jdbcOffsetDateTime.toInstant() ), - * but this won't always work since Timestamp.from() assumes the number of - * milliseconds since the epoch means the same thing in Timestamp and Instant, - * but it doesn't, in particular before 1900. - */ + // Workaround for HHH-13266 (JDK-8061577). + // Ideally, we'd want to use Timestamp.from( jdbcOffsetDateTime.toInstant() ), + // but this won't always work since Timestamp.from() assumes the number of + // milliseconds since the epoch means the same thing in Timestamp and Instant, + // but it doesn't, in particular before 1900. return (X) Timestamp.valueOf( jdbcOffsetDateTime.toLocalDateTime() ); } @@ -171,28 +169,26 @@ public OffsetTime wrap(X value, WrapperOptions options) { return offsetDateTime.toOffsetTime(); } - /* - * Also, in order to fix HHH-13357, and to be consistent with the conversion to Time (see above), - * we set the offset to the current offset of the JVM (OffsetDateTime.now().getOffset()). - * This is different from setting the *zone* to the current *zone* of the JVM (ZoneId.systemDefault()), - * since a zone has a varying offset over time, - * thus the zone might have a different offset for the given timezone than it has for the current date/time. - * For example, if the timestamp represents 1970-01-01TXX:YY, - * and the JVM is set to use Europe/Paris as a timezone, and the current time is 2019-04-16-08:53, - * then applying the JVM timezone to the timestamp would result in the offset +01:00, - * but applying the JVM offset would result in the offset +02:00, since DST is in effect at 2019-04-16-08:53. - * - * Of course none of this would be a problem if we just stored the offset in the database, - * but I guess there are historical reasons that explain why we don't. - */ + // Also, in order to fix HHH-13357, and to be consistent with the conversion to Time (see above), + // we set the offset to the current offset of the JVM (OffsetDateTime.now().getOffset()). This is + // different from setting the *zone* to the current *zone* of the JVM (ZoneId.systemDefault()), + // since a zone has a varying offset over time; thus the zone might have a different offset for the + // given timezone than it has for the current date/time. For example, if the timestamp represents + // 1970-01-01TXX:YY, and the JVM is set to use Europe/Paris as a timezone, and the current time is + // 2019-04-16-08:53, then applying the JVM timezone to the timestamp would result in the offset + // +01:00, but applying the JVM offset would result in the offset +02:00, since DST is in effect at + // 2019-04-16-08:53. + // + // Of course, none of this would be a problem if we just stored the offset in the database, but I + // guess there are historical reasons that explain why we don't. // for legacy types, we assume that the JDBC timezone is passed to JDBC // (since PS.setTime() and friends do accept a timezone passed as a Calendar) if (value instanceof Time time) { - final OffsetTime offsetTime = time.toLocalTime() - .atOffset( getCurrentJdbcOffset( options) ) - .withOffsetSameInstant( getCurrentSystemOffset() ); + final var offsetTime = + time.toLocalTime().atOffset( getCurrentJdbcOffset( options) ) + .withOffsetSameInstant( getCurrentSystemOffset() ); long millis = time.getTime() % 1000; if ( millis == 0 ) { return offsetTime; @@ -206,13 +202,11 @@ public OffsetTime wrap(X value, WrapperOptions options) { } if (value instanceof Timestamp timestamp) { - /* - * Workaround for HHH-13266 (JDK-8061577). - * Ideally we'd want to use OffsetDateTime.ofInstant( ts.toInstant(), ... ), - * but this won't always work since ts.toInstant() assumes the number of - * milliseconds since the epoch means the same thing in Timestamp and Instant, - * but it doesn't, in particular before 1900. - */ + // Workaround for HHH-13266 (JDK-8061577). + // Ideally, we'd want to use OffsetDateTime.ofInstant( ts.toInstant(), ... ), + // but this won't always work since ts.toInstant() assumes the number of + // milliseconds since the epoch means the same thing in Timestamp and Instant, + // but it doesn't, in particular before 1900. return timestamp.toLocalDateTime().toLocalTime().atOffset( getCurrentJdbcOffset(options) ) .withOffsetSameInstant( getCurrentSystemOffset() ); } @@ -235,12 +229,10 @@ public OffsetTime wrap(X value, WrapperOptions options) { } private static ZoneOffset getCurrentJdbcOffset(WrapperOptions options) { - if ( options.getJdbcTimeZone() != null ) { - return OffsetDateTime.now().atZoneSameInstant( options.getJdbcTimeZone().toZoneId() ).getOffset(); - } - else { - return getCurrentSystemOffset(); - } + final var jdbcTimeZone = options.getJdbcTimeZone(); + return jdbcTimeZone != null + ? OffsetDateTime.now().atZoneSameInstant( jdbcTimeZone.toZoneId() ).getOffset() + : getCurrentSystemOffset(); } private static ZoneOffset getCurrentSystemOffset() { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java index 22cde050e664..cfd3b950e58d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java @@ -54,10 +54,9 @@ public TemporalType getPrecision() { @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators stdIndicators) { - if ( stdIndicators.isPreferJavaTimeJdbcTypesEnabled() ) { - return stdIndicators.getJdbcType( SqlTypes.ZONED_DATE_TIME ); - } - return stdIndicators.getJdbcType( stdIndicators.getDefaultZonedTimestampSqlType() ); + return stdIndicators.isPreferJavaTimeJdbcTypesEnabled() + ? stdIndicators.getJdbcType( SqlTypes.ZONED_DATE_TIME ) + : stdIndicators.getJdbcType( stdIndicators.getDefaultZonedTimestampSqlType() ); } @Override @SuppressWarnings("unchecked") @@ -104,19 +103,21 @@ public X unwrap(ZonedDateTime zonedDateTime, Class type, WrapperOptions o } if ( Timestamp.class.isAssignableFrom( type ) ) { - /* - * This works around two bugs: - * - HHH-13266 (JDK-8061577): around and before 1900, - * the number of milliseconds since the epoch does not mean the same thing - * for java.util and java.time, so conversion must be done using the year, month, day, hour, etc. - * - HHH-13379 (JDK-4312621): after 1908 (approximately), - * Daylight Saving Time introduces ambiguity in the year/month/day/hour/etc representation once a year - * (on DST end), so conversion must be done using the number of milliseconds since the epoch. - * - around 1905, both methods are equally valid, so we don't really care which one is used. - */ + // This works around two bugs: + // - HHH-13266 (JDK-8061577): around and before 1900, + // the number of milliseconds since the epoch does not mean the same thing + // for java.util and java.time, so conversion must be done using the year, + // month, day, hour, etc. + // - HHH-13379 (JDK-4312621): after 1908 (approximately), + // Daylight Saving Time introduces ambiguity in the year/month/day/hour/etc + // representation once a year (on DST end), so conversion must be done using + // the number of milliseconds since the epoch. + // - around 1905, both methods are equally valid, so we don't really care which + // one is used. if ( zonedDateTime.getYear() < 1905 ) { return (X) Timestamp.valueOf( - zonedDateTime.withZoneSameInstant( ZoneId.systemDefault() ).toLocalDateTime() + zonedDateTime.withZoneSameInstant( ZoneId.systemDefault() ) + .toLocalDateTime() ); } else { @@ -162,22 +163,20 @@ public ZonedDateTime wrap(X value, WrapperOptions options) { } if (value instanceof Timestamp timestamp) { - /* - * This works around two bugs: - * - HHH-13266 (JDK-8061577): around and before 1900, - * the number of milliseconds since the epoch does not mean the same thing - * for java.util and java.time, so conversion must be done using the year, month, day, hour, etc. - * - HHH-13379 (JDK-4312621): after 1908 (approximately), - * Daylight Saving Time introduces ambiguity in the year/month/day/hour/etc representation once a year - * (on DST end), so conversion must be done using the number of milliseconds since the epoch. - * - around 1905, both methods are equally valid, so we don't really care which one is used. - */ - if ( timestamp.getYear() < 5 ) { // Timestamp year 0 is 1900 - return timestamp.toLocalDateTime().atZone( ZoneId.systemDefault() ); - } - else { - return timestamp.toInstant().atZone( ZoneId.systemDefault() ); - } + // This works around two bugs: + // - HHH-13266 (JDK-8061577): around and before 1900, + // the number of milliseconds since the epoch does not mean the same thing + // for java.util and java.time, so conversion must be done using the year, + // month, day, hour, etc. + // - HHH-13379 (JDK-4312621): after 1908 (approximately), + // Daylight Saving Time introduces ambiguity in the year/month/day/hour/etc + // representation once a year (on DST end), so conversion must be done using + // the number of milliseconds since the epoch. + // - around 1905, both methods are equally valid, so we don't really care which + // one is used. + return timestamp.getYear() < 5 // Timestamp year 0 is 1900 + ? timestamp.toLocalDateTime().atZone( ZoneId.systemDefault() ) + : timestamp.toInstant().atZone( ZoneId.systemDefault() ); } if (value instanceof Date date) { From 52c6583717c8ad150e351852fea119c0e759e89b Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 6 Dec 2025 11:25:49 +0100 Subject: [PATCH 034/181] misc cleanups in SessionImpl --- .../SharedStatelessSessionBuilderImpl.java | 2 +- .../AbstractSharedSessionContract.java | 3 +- .../org/hibernate/internal/SessionImpl.java | 111 +++++++++--------- .../hibernate/internal/SessionLogging.java | 4 + .../internal/StatelessSessionImpl.java | 4 +- ...leSharedStatelessSessionBuildingTests.java | 4 +- 6 files changed, 68 insertions(+), 60 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedStatelessSessionBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedStatelessSessionBuilderImpl.java index 585579dbf1a8..9af5a6c25f93 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedStatelessSessionBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedStatelessSessionBuilderImpl.java @@ -92,7 +92,7 @@ public SharedStatelessSessionBuilder interceptor() { @Override public SharedStatelessSessionBuilder statementInspector() { - this.statementInspector = original.getJdbcSessionContext().getStatementInspector(); + statementInspector = original.getJdbcSessionContext().getStatementInspector(); return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index 2474372bc614..907676485197 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -260,7 +260,8 @@ public SharedStatelessSessionBuilder statelessWithOptions() { @Override protected StatelessSessionImplementor createStatelessSession() { return new StatelessSessionImpl( factory, - new SessionCreationOptionsAdaptor( factory, this, AbstractSharedSessionContract.this ) ); + new SessionCreationOptionsAdaptor( factory, this, + AbstractSharedSessionContract.this ) ); } }; } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 232f193263c5..148780126c9a 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -158,8 +158,8 @@ public class SessionImpl implements Serializable, SharedSessionContractImplementor, JdbcSessionOwner, SessionImplementor, EventSource, TransactionCoordinatorBuilder.Options, WrapperOptions, LoadAccessContext { - // Defaults to null which means the properties are the default - // as defined in FastSessionServices#defaultSessionProperties + // Defaults to null, meaning the properties + // are the default properties of the factory. private Map properties; private transient ActionQueue actionQueue; @@ -191,8 +191,6 @@ public SessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) { actionQueue = createActionQueue(); eventListenerGroups = factory.getEventListenerGroups(); - flushMode = options.getInitialSessionFlushMode(); - autoClear = options.shouldAutoClear(); autoClose = options.shouldAutoClose(); @@ -202,13 +200,10 @@ public SessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) { loadQueryInfluencers = new LoadQueryInfluencers( factory, options ); - // NOTE : pulse() already handles auto-join-ability correctly + // NOTE: pulse() already handles auto-join-ability correctly getTransactionCoordinator().pulse(); - // do not override explicitly set flush mode ( SessionBuilder#flushMode() ) - if ( getHibernateFlushMode() == null ) { - setHibernateFlushMode( getInitialFlushMode() ); - } + flushMode = getInitialFlushMode( options ); setUpMultitenancy( factory, loadQueryInfluencers ); @@ -247,10 +242,16 @@ private static void setUpTransactionCompletionProcesses( } } - private FlushMode getInitialFlushMode() { - return properties == null - ? getSessionFactoryOptions().getInitialSessionFlushMode() - : ConfigurationHelper.getFlushMode( getSessionProperty( HINT_FLUSH_MODE ), FlushMode.AUTO ); + private FlushMode getInitialFlushMode(SessionCreationOptions options) { + final var initialSessionFlushMode = options.getInitialSessionFlushMode(); + if ( initialSessionFlushMode != null ) { + return initialSessionFlushMode; + } + else { + return properties == null + ? getSessionFactoryOptions().getInitialSessionFlushMode() + : ConfigurationHelper.getFlushMode( properties.get( HINT_FLUSH_MODE ), FlushMode.AUTO ); + } } protected PersistenceContext createPersistenceContext(SessionCreationOptions options) { @@ -351,7 +352,6 @@ public void clear() { private void internalClear() { persistenceContext.clear(); actionQueue.clear(); - eventListenerGroups.eventListenerGroup_CLEAR .fireLazyEventOnEachListener( this::createClearEvent, ClearEventListener::onClear ); } @@ -392,7 +392,7 @@ public void closeWithoutOpenChecks() { else { // In the JPA bootstrap, if the session is closed // before the transaction commits, we just mark the - // session as closed, and set waitingForAutoClose. + // session as closed and set waitingForAutoClose. // This method will be called a second time from // afterTransactionCompletion when the transaction // commits, and the session will be closed for real. @@ -405,10 +405,12 @@ public void closeWithoutOpenChecks() { } } finally { - // E.g. when we are in the JTA context the session can get closed while the transaction is still active - // and JTA will call the AfterCompletion itself. Hence, we don't want to clear out the action queue callbacks at this point: - if ( !getTransactionCoordinator().isTransactionActive() && actionQueue.hasAfterTransactionActions() ) { - SESSION_LOGGER.warn( "Closing session with unprocessed clean up bulk operations, forcing their execution" ); + // E.g. When we are in the JTA context, the session can get closed while the + // transaction is still active and JTA will call the AfterCompletion itself. + // Hence, we don't want to clear out the action queue callbacks at this point: + if ( !getTransactionCoordinator().isTransactionActive() + && actionQueue.hasAfterTransactionActions() ) { + SESSION_LOGGER.closingSessionWithUnprocessedBulkOperations(); actionQueue.executePendingBulkOperationCleanUpActions(); } final var statistics = getSessionFactory().getStatistics(); @@ -489,8 +491,8 @@ else if ( isClosed() ) { return false; } else { - // JPA technically requires that this be a PersistentUnityTransactionType#JTA to work, - // but we do not assert that here: + // JPA requires PersistentUnitTransactionType.JTA, + // for this, but we do not assert that here: return isAutoCloseSessionEnabled(); // && getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta(); } @@ -557,7 +559,9 @@ public Object getEntityUsingInterceptor(EntityKey key) { // logically, is PersistentContext the "thing" to which an interceptor gets attached? final Object result = persistenceContext.getEntity( key ); if ( result == null ) { - final Object newObject = getInterceptor().getEntity( key.getEntityName(), key.getIdentifier() ); + final Object newObject = + getInterceptor() + .getEntity( key.getEntityName(), key.getIdentifier() ); if ( newObject != null ) { lock( newObject, LockMode.NONE ); } @@ -870,8 +874,8 @@ public void removeOrphanBeforeUpdates(String entityName, Object child) { private void logRemoveOrphanBeforeUpdates(String timing, String entityName, Object entity) { if ( SESSION_LOGGER.isTraceEnabled() ) { final var entityEntry = persistenceContext.getEntry( entity ); - final String entityInfo = entityEntry == null ? entityName : infoString( entityName, entityEntry.getId() ); - SESSION_LOGGER.removeOrphanBeforeUpdates( timing, entityInfo ); + SESSION_LOGGER.removeOrphanBeforeUpdates( timing, + entityEntry == null ? entityName : infoString( entityName, entityEntry.getId() ) ); } } @@ -933,7 +937,7 @@ private void setMultiIdentifierLoadAccessOptions(FindOption[] options, Multi CacheRetrieveMode retrieveMode = getCacheRetrieveMode(); LockOptions lockOptions = copySessionLockOptions(); int batchSize = -1; - for ( FindOption option : options ) { + for ( var option : options ) { if ( option instanceof CacheStoreMode cacheStoreMode ) { storeMode = cacheStoreMode; } @@ -1553,7 +1557,8 @@ public void forceFlush(EntityEntry entityEntry) { @Override public void forceFlush(EntityKey key) { if ( SESSION_LOGGER.isTraceEnabled() ) { - SESSION_LOGGER.flushingToForceDeletion( infoString( key.getPersister(), key.getIdentifier(), getFactory() ) ); + SESSION_LOGGER.flushingToForceDeletion( + infoString( key.getPersister(), key.getIdentifier(), getFactory() ) ); } if ( persistenceContext.getCascadeLevel() > 0 ) { @@ -2124,7 +2129,7 @@ private boolean isTransactionFlushable() { return true; } else { - final TransactionStatus status = currentTransaction.getStatus(); + final var status = currentTransaction.getStatus(); return status == TransactionStatus.ACTIVE || status == TransactionStatus.COMMITTING; } @@ -2190,13 +2195,13 @@ private T find(Class entityClass, Object primaryKey, LockOptions lockOpti .load( primaryKey ); } catch ( FetchNotFoundException e ) { - // This may happen if the entity has an associations mapped with + // This may happen if the entity has an association mapped with // @NotFound(action = NotFoundAction.EXCEPTION) and this associated // entity is not found throw e; } catch ( EntityFilterException e ) { - // This may happen if the entity has an associations which is + // This may happen if the entity has an association which is // filtered by a FilterDef and this associated entity is not found throw e; } @@ -2256,7 +2261,7 @@ private void setLoadAccessOptions(FindOption[] options, IdentifierLoadAccess CacheStoreMode storeMode = getCacheStoreMode(); CacheRetrieveMode retrieveMode = getCacheRetrieveMode(); LockOptions lockOptions = copySessionLockOptions(); - for ( FindOption option : options ) { + for ( var option : options ) { if ( option instanceof CacheStoreMode cacheStoreMode ) { storeMode = cacheStoreMode; } @@ -2295,15 +2300,15 @@ else if ( option instanceof ReadOnlyMode ) { loadAccess.withReadOnly( option == ReadOnlyMode.READ_ONLY ); } else if ( option instanceof FindMultipleOption findMultipleOption ) { - throw new IllegalArgumentException( "Option '" + findMultipleOption + "' can only be used in 'findMultiple()'" ); + throw new IllegalArgumentException( "Option '" + findMultipleOption + + "' can only be used in 'findMultiple()'" ); } } - if ( lockOptions.getLockMode().isPessimistic() ) { - if ( lockOptions.getTimeOut() == WAIT_FOREVER_MILLI ) { - final Object factoryHint = getFactory().getProperties().get( HINT_SPEC_LOCK_TIMEOUT ); - if ( factoryHint != null ) { - lockOptions.setTimeOut( Timeouts.fromHint( factoryHint ) ); - } + if ( lockOptions.getLockMode().isPessimistic() + && lockOptions.getTimeOut() == WAIT_FOREVER_MILLI ) { + final Object factoryHint = getFactory().getProperties().get( HINT_SPEC_LOCK_TIMEOUT ); + if ( factoryHint != null ) { + lockOptions.setTimeOut( Timeouts.fromHint( factoryHint ) ); } } loadAccess.with( lockOptions ).with( interpretCacheMode( storeMode, retrieveMode ) ); @@ -2578,25 +2583,23 @@ public LockModeType getLockMode(Object entity) { @Override public void setProperty(String propertyName, Object value) { checkOpen(); - - if ( !( value instanceof Serializable ) ) { - SESSION_LOGGER.nonSerializableProperty( propertyName ); - return; - } - - if ( propertyName == null ) { - SESSION_LOGGER.nullPropertyKey(); - return; + if ( value instanceof Serializable ) { + if ( propertyName != null ) { // store property for future reference: + if ( properties == null ) { + properties = computeCurrentProperties(); + } + properties.put( propertyName, value ); + // now actually update the setting + // if it's one that affects this Session + interpretProperty( propertyName, value ); + } + else { + SESSION_LOGGER.nullPropertyKey(); + } } - - // store property for future reference: - if ( properties == null ) { - properties = computeCurrentProperties(); + else { + SESSION_LOGGER.nonSerializableProperty( propertyName ); } - properties.put( propertyName, value ); - - // now actually update the setting, if it's one which affects this Session - interpretProperty( propertyName, value ); } private void interpretProperty(String propertyName, Object value) { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionLogging.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionLogging.java index cddf971d6999..a82a6fa12e56 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionLogging.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionLogging.java @@ -145,6 +145,10 @@ public interface SessionLogging extends BasicLogger { @Message(id = 90010107, value = "Exception in interceptor afterTransactionCompletion()") void exceptionInAfterTransactionCompletionInterceptor(@Cause Throwable e); + @LogMessage(level = WARN) + @Message(id = 90010108, value = "Closing session with unprocessed clean up bulk operations, forcing their execution") + void closingSessionWithUnprocessedBulkOperations(); + // StatelessSession-specific @LogMessage(level = TRACE) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java index f38757327a72..778a14a1c543 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java @@ -152,8 +152,8 @@ public StatelessSessionImpl(SessionFactoryImpl factory, SessionCreationOptions o influencers = new LoadQueryInfluencers( getFactory() ); eventListenerGroups = factory.getEventListenerGroups(); setUpMultitenancy( factory, influencers ); - // a nonzero batch size forces use of write-behind - // therefore ignore the value of hibernate.jdbc.batch_size + // A nonzero batch size forces the use of write-behind + // Therefore, ignore the value of hibernate.jdbc.batch_size setJdbcBatchSize( 0 ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/shared/SimpleSharedStatelessSessionBuildingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/shared/SimpleSharedStatelessSessionBuildingTests.java index 39764908cb59..6bbb89e18bc4 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/shared/SimpleSharedStatelessSessionBuildingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/stateless/shared/SimpleSharedStatelessSessionBuildingTests.java @@ -135,9 +135,9 @@ void testUsage(SessionFactoryScope factoryScope) { session.insert( new Something( 2, "first" ) ); assertSame( session.getTransaction(), statelessSession.getTransaction() ); assertSame( ((StatelessSessionImplementor) session).getJdbcCoordinator(), - ((StatelessSessionImplementor) statelessSession).getJdbcCoordinator() ); + statelessSession.getJdbcCoordinator() ); assertSame( ((StatelessSessionImplementor) session).getTransactionCompletionCallbacksImplementor(), - ((StatelessSessionImplementor) statelessSession).getTransactionCompletionCallbacksImplementor() ); + statelessSession.getTransactionCompletionCallbacksImplementor() ); } } ); assertThat( sqlCollector.getSqlQueries() ).hasSize( 1 ); From 17eccc31331bdc863e6f7a7b5ce84aa7b324e2c3 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 6 Dec 2025 12:20:45 +0100 Subject: [PATCH 035/181] fix impl of EntityManager.getProperties() to comply with Javadoc --- .../org/hibernate/internal/SessionImpl.java | 25 +++++++++++-------- .../orm/test/jpa/EntityManagerTest.java | 12 ++++----- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 148780126c9a..82b2f2e250a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -86,7 +86,6 @@ import static java.lang.Boolean.parseBoolean; import static java.lang.Integer.parseInt; import static java.lang.System.currentTimeMillis; -import static java.util.Collections.unmodifiableMap; import static org.hibernate.CacheMode.fromJpaModes; import static org.hibernate.Timeouts.WAIT_FOREVER_MILLI; import static org.hibernate.cfg.AvailableSettings.CRITERIA_COPY_TREE; @@ -2584,13 +2583,14 @@ public LockModeType getLockMode(Object entity) { public void setProperty(String propertyName, Object value) { checkOpen(); if ( value instanceof Serializable ) { - if ( propertyName != null ) { // store property for future reference: + if ( propertyName != null ) { + // store property for future reference if ( properties == null ) { - properties = computeCurrentProperties(); + properties = getInitialProperties(); } properties.put( propertyName, value ); - // now actually update the setting - // if it's one that affects this Session + // now actually update the setting if + // it's one that affects this Session interpretProperty( propertyName, value ); } else { @@ -2662,7 +2662,7 @@ private void interpretProperty(String propertyName, Object value) { } } - private Map computeCurrentProperties() { + private Map getInitialProperties() { final var map = new HashMap<>( getDefaultProperties() ); //The FLUSH_MODE is always set at Session creation time, //so it needs special treatment to not eagerly initialize this Map: @@ -2672,10 +2672,15 @@ private Map computeCurrentProperties() { @Override public Map getProperties() { - if ( properties == null ) { - properties = computeCurrentProperties(); - } - return unmodifiableMap( properties ); + // EntityManager Javadoc implies that the + // returned map should be a mutable copy, + // not an unmodifiable map. There's no + // good reason to cache the initial + // properties, since we have to copy them + // each time this method is called. + return properties == null + ? getInitialProperties() + : new HashMap<>( properties ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/EntityManagerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/EntityManagerTest.java index 84001c3c01b8..c11adf14898d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/EntityManagerTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/EntityManagerTest.java @@ -307,13 +307,11 @@ public void testGet() throws Exception { @Test public void testGetProperties() { inEntityManager( entityManager -> { - Map properties = entityManager.getProperties(); - assertNotNull( properties ); - assertThrows( - UnsupportedOperationException.class, - () -> properties.put( "foo", "bar" ) - ); - assertTrue( properties.containsKey( HibernateHints.HINT_FLUSH_MODE ) ); + assertNotNull( entityManager.getProperties() ); + assertTrue( entityManager.getProperties().containsKey( HibernateHints.HINT_FLUSH_MODE ) ); + // according to Javadoc, getProperties() returns mutable copy + entityManager.getProperties().put( "foo", "bar" ); + assertFalse( entityManager.getProperties().containsKey( "foo" ) ); } ); } From ed05cdf449f03fc2f119bf2ecd89f7ebec538612 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 6 Dec 2025 12:34:38 +0100 Subject: [PATCH 036/181] improved explanations of constructor stuff for repositories --- .../processor/annotation/AnnotationMetaEntity.java | 13 ++++++------- .../processor/annotation/DefaultConstructor.java | 7 +++++++ .../processor/annotation/RepositoryConstructor.java | 6 +++++- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java index 045498027821..53c965d29caf 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java @@ -936,13 +936,12 @@ void addEventBus() { /** * For usage with CDI, but outside Quarkus, Jakarta Data - * repositories use {@code @PersistenceUnit} to obtain an - * {@code EntityManagerFactory} via field injection. So in - * that case we will need a {@link DefaultConstructor default - * constructor}. We don't do this in Quarkus, because there - * we can just inject the {@code StatelessSession} directly, - * and so in Quarkus we don't need the default constructor - * at all. + * repositories use {@code @PersistenceUnit} to obtain + * an {@code EntityManagerFactory} via field injection. + * So here we need a {@linkplain DefaultConstructor + * default constructor}. We don't need one in Quarkus, + * because in Quarkus we can inject a container-managed + * {@code StatelessSession} directly. */ boolean needsDefaultConstructor() { return jakartaDataRepository diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DefaultConstructor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DefaultConstructor.java index 54cd106ae92c..c874d4fba420 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DefaultConstructor.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DefaultConstructor.java @@ -21,6 +21,13 @@ /** * Used by the container to instantiate a Jakarta Data repository. + * This is a constructor with no parameters, used to instantiate + * a repository which then uses field injection to obtain its + * dependencies. By contrast, a {@link RepositoryConstructor} has + * a parameter which accepts the session as an argument, allowing + * direct instantiation or constructor injection. This class is + * only needed because {@code @PersistenceUnit} is incompatible + * with CDI-style constructor injection. * * @author Gavin King */ diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/RepositoryConstructor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/RepositoryConstructor.java index 20727f6cfb95..007d7426a5a5 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/RepositoryConstructor.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/RepositoryConstructor.java @@ -14,7 +14,11 @@ import static org.hibernate.processor.util.Constants.NONNULL; /** - * A general purpose constructor which accepts the session. + * A general purpose constructor which accepts the session as an + * argument. This constructor is compatible with use via direct + * instantiation or CDI-style constructor injection. By contrast, + * {@link DefaultConstructor} is used to instantiate a repository + * which obtains its session using field injection. * * @author Gavin King */ From 311c840e7dcfed21aac8337e9dce150378b1c653 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 6 Dec 2025 23:35:41 +0100 Subject: [PATCH 037/181] use Class.cast() to remove all the horrible unchecked type casts in the JavaTypes --- .../type/AbstractStandardBasicType.java | 94 ++++++++----------- .../java/org/hibernate/type/EntityType.java | 4 +- .../java/AbstractClassJavaType.java | 2 +- .../type/descriptor/java/ArrayJavaType.java | 13 +-- .../descriptor/java/BigDecimalJavaType.java | 24 +++-- .../descriptor/java/BigIntegerJavaType.java | 24 +++-- .../type/descriptor/java/BlobJavaType.java | 20 ++-- .../type/descriptor/java/BooleanJavaType.java | 43 +++++---- .../java/BooleanPrimitiveArrayJavaType.java | 11 +-- .../descriptor/java/ByteArrayJavaType.java | 33 ++++--- .../type/descriptor/java/ByteJavaType.java | 20 ++-- .../descriptor/java/CalendarDateJavaType.java | 11 +-- .../descriptor/java/CalendarJavaType.java | 11 +-- .../descriptor/java/CalendarTimeJavaType.java | 11 +-- .../java/CharacterArrayJavaType.java | 18 ++-- .../descriptor/java/CharacterJavaType.java | 12 ++- .../type/descriptor/java/ClassJavaType.java | 10 +- .../type/descriptor/java/ClobJavaType.java | 20 ++-- .../descriptor/java/CurrencyJavaType.java | 10 +- .../type/descriptor/java/DateJavaType.java | 18 ++-- .../type/descriptor/java/DoubleJavaType.java | 24 +++-- .../java/DoublePrimitiveArrayJavaType.java | 16 ++-- .../descriptor/java/DurationJavaType.java | 16 ++-- .../type/descriptor/java/EnumJavaType.java | 13 ++- .../type/descriptor/java/FloatJavaType.java | 24 +++-- .../java/FloatPrimitiveArrayJavaType.java | 16 ++-- .../descriptor/java/InetAddressJavaType.java | 7 +- .../type/descriptor/java/InstantJavaType.java | 24 +++-- .../type/descriptor/java/IntegerJavaType.java | 24 +++-- .../java/IntegerPrimitiveArrayJavaType.java | 16 ++-- .../type/descriptor/java/JavaType.java | 34 +++++-- .../descriptor/java/JdbcDateJavaType.java | 5 + .../descriptor/java/JdbcTimeJavaType.java | 5 + .../java/JdbcTimestampJavaType.java | 5 + .../descriptor/java/LocalDateJavaType.java | 18 ++-- .../java/LocalDateTimeJavaType.java | 20 ++-- .../descriptor/java/LocalTimeJavaType.java | 20 ++-- .../type/descriptor/java/LocaleJavaType.java | 10 +- .../type/descriptor/java/LongJavaType.java | 24 +++-- .../java/LongPrimitiveArrayJavaType.java | 16 ++-- .../java/MutableMutabilityPlan.java | 2 +- .../type/descriptor/java/NClobJavaType.java | 20 ++-- .../descriptor/java/ObjectArrayJavaType.java | 8 +- .../type/descriptor/java/ObjectJavaType.java | 8 +- .../java/OffsetDateTimeJavaType.java | 28 +++--- .../descriptor/java/OffsetTimeJavaType.java | 26 ++--- .../java/PrimitiveByteArrayJavaType.java | 16 ++-- .../java/PrimitiveCharacterArrayJavaType.java | 18 ++-- .../descriptor/java/SerializableJavaType.java | 24 ++--- .../type/descriptor/java/ShortJavaType.java | 20 ++-- .../java/ShortPrimitiveArrayJavaType.java | 16 ++-- .../type/descriptor/java/StringJavaType.java | 22 +++-- .../descriptor/java/TimeZoneJavaType.java | 10 +- .../type/descriptor/java/UUIDJavaType.java | 12 ++- .../type/descriptor/java/UrlJavaType.java | 10 +- .../type/descriptor/java/YearJavaType.java | 14 ++- .../type/descriptor/java/ZoneIdJavaType.java | 10 +- .../descriptor/java/ZoneOffsetJavaType.java | 12 ++- .../java/ZonedDateTimeJavaType.java | 28 +++--- .../java/spi/BasicCollectionJavaType.java | 17 ++-- .../java/spi/CollectionJavaType.java | 6 +- .../java/spi/EmbeddableAggregateJavaType.java | 17 ++-- .../descriptor/java/spi/EntityJavaType.java | 11 ++- .../java/spi/FormatMapperBasedJavaType.java | 14 ++- .../java/spi/UnknownBasicJavaType.java | 9 +- .../type/descriptor/jdbc/ObjectJdbcType.java | 7 +- .../type/descriptor/jdbc/StructHelper.java | 3 +- .../type/descriptor/jdbc/StructJdbcType.java | 6 +- .../descriptor/jdbc/XmlArrayJdbcType.java | 3 +- .../type/descriptor/jdbc/XmlJdbcType.java | 6 +- .../jdbc/spi/BasicJdbcLiteralFormatter.java | 21 ++--- .../CompositeUserTypeJavaTypeWrapper.java | 5 +- .../internal/UserTypeJavaTypeWrapper.java | 15 ++- 73 files changed, 679 insertions(+), 511 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java b/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java index d41f3de19ffc..9c6434711ef7 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java @@ -55,18 +55,18 @@ public abstract class AbstractStandardBasicType public AbstractStandardBasicType(JdbcType jdbcType, JavaType javaType) { this.jdbcType = jdbcType; - this.sqlTypes = new int[] { jdbcType.getDdlTypeCode() }; this.javaType = javaType; + sqlTypes = new int[] { jdbcType.getDdlTypeCode() }; - this.jdbcValueBinder = jdbcType.getBinder( javaType ); - this.jdbcValueExtractor = jdbcType.getExtractor( javaType ); - this.jdbcLiteralFormatter = jdbcType.getJdbcLiteralFormatter( javaType ); + jdbcValueBinder = jdbcType.getBinder( javaType ); + jdbcValueExtractor = jdbcType.getExtractor( javaType ); + jdbcLiteralFormatter = jdbcType.getJdbcLiteralFormatter( javaType ); //A very simple dispatch optimisation, make these a constant: - this.javaTypeClass = javaType.getJavaTypeClass(); - this.mutabilityPlan = javaType.getMutabilityPlan(); - this.javatypeComparator = javaType.getComparator(); - this.typeForEqualsHashCode = javaType.useObjectEqualsHashCode() ? null : this; + javaTypeClass = javaType.getJavaTypeClass(); + mutabilityPlan = javaType.getMutabilityPlan(); + javatypeComparator = javaType.getComparator(); + typeForEqualsHashCode = javaType.useObjectEqualsHashCode() ? null : this; } @Override @@ -174,7 +174,6 @@ public final boolean isEqual(Object x, Object y, SessionFactoryImplementor facto } @Override - @SuppressWarnings("unchecked") public boolean isEqual(Object one, Object another) { if ( one == another ) { return true; @@ -186,19 +185,15 @@ else if ( typeForEqualsHashCode == null ) { return one.equals( another ); } else { - return javaType.areEqual( (T) one, (T) another ); + return javaType.areEqual( javaType.cast( one ), javaType.cast( another ) ); } } @Override - @SuppressWarnings("unchecked") - public int getHashCode(Object x) { - if ( typeForEqualsHashCode == null ) { - return x.hashCode(); - } - else { - return javaType.extractHashCode( (T) x ); - } + public int getHashCode(Object object) { + return typeForEqualsHashCode == null + ? object.hashCode() + : javaType.extractHashCode( javaType.cast( object ) ); } @Override @@ -212,9 +207,8 @@ public final int getHashCode(Object x, SessionFactoryImplementor factory) { } @Override - @SuppressWarnings("unchecked") public final int compare(Object x, Object y) { - return this.javatypeComparator.compare( (T) x, (T) y ); + return this.javatypeComparator.compare( javaType.cast( x ) , javaType.cast( y ) ); } @Override @@ -228,9 +222,11 @@ public final boolean isDirty(Object old, Object current, boolean[] checkable, Sh } protected final boolean isDirty(Object old, Object current) { - // MutableMutabilityPlan.INSTANCE is a special plan for which we always have to assume the value is dirty, - // because we can't actually copy a value, but have no knowledge about the mutability of the java type - return getMutabilityPlan() == MutableMutabilityPlan.INSTANCE || !isSame( old, current ); + // MutableMutabilityPlan.INSTANCE is a special plan for which we always + // have to assume the value is dirty, because we can't actually copy a + // value, but have no knowledge about the mutability of the java type + return getMutabilityPlan() == MutableMutabilityPlan.INSTANCE + || !isSame( old, current ); } @Override @@ -247,22 +243,23 @@ public final void nullSafeSet( PreparedStatement st, Object value, int index, - final SharedSessionContractImplementor session) throws SQLException { - //noinspection unchecked - nullSafeSet( st, (T) value, index, (WrapperOptions) session ); + final SharedSessionContractImplementor session) + throws SQLException { + nullSafeSet( st, javaType.cast( value ) , index, (WrapperOptions) session ); } - protected void nullSafeSet(PreparedStatement st, T value, int index, WrapperOptions options) throws SQLException { + protected void nullSafeSet(PreparedStatement st, T value, int index, WrapperOptions options) + throws SQLException { getJdbcValueBinder().bind( st, value, index, options ); } @Override - @SuppressWarnings("unchecked") public final String toLoggableString(Object value, SessionFactoryImplementor factory) { - if ( value == LazyPropertyInitializer.UNFETCHED_PROPERTY || !Hibernate.isInitialized( value ) ) { - return ""; - } - return javaType.extractLoggableRepresentation( (T) value ); + return value == LazyPropertyInitializer.UNFETCHED_PROPERTY + || !Hibernate.isInitialized( value ) + ? "" + : javaType.extractLoggableRepresentation( + javaType.coerce( value, factory::getTypeConfiguration ) ); } @Override @@ -271,9 +268,8 @@ public final boolean isMutable() { } @Override - @SuppressWarnings("unchecked") public final Object deepCopy(Object value, SessionFactoryImplementor factory) { - return deepCopy( (T) value ); + return deepCopy( javaType.cast( value ) ); } protected final T deepCopy(T value) { @@ -281,9 +277,8 @@ protected final T deepCopy(T value) { } @Override - @SuppressWarnings("unchecked") public final Serializable disassemble(Object value, SharedSessionContractImplementor session, Object owner) throws HibernateException { - return getMutabilityPlan().disassemble( (T) value, session ); + return getMutabilityPlan().disassemble( javaType.cast( value ) , session ); } @Override @@ -296,16 +291,14 @@ public final void beforeAssemble(Serializable cached, SharedSessionContractImple } @Override - @SuppressWarnings("unchecked") public final Object replace(Object original, Object target, SharedSessionContractImplementor session, Object owner, Map copyCache) { return original == null && target == null ? null - : javaType.getReplacement( (T) original, (T) target, session ); + : javaType.getReplacement( javaType.cast( original ) , javaType.cast( target ) , session ); } @Override - @SuppressWarnings("unchecked") public Object replace( Object original, Object target, @@ -314,6 +307,9 @@ public Object replace( Map copyCache, ForeignKeyDirection foreignKeyDirection) { return ForeignKeyDirection.FROM_PARENT == foreignKeyDirection + // TODO: use cast() .. currently failing on embeddable discriminators where + // the concrete class is passed in instead of its disciminator value +// ? javaType.getReplacement( javaType.cast( original ) , javaType.cast( target ) , session ) ? javaType.getReplacement( (T) original, (T) target, session ) : target; } @@ -325,20 +321,12 @@ public boolean canDoExtraction() { @Override public T extract(CallableStatement statement, int startIndex, final SharedSessionContractImplementor session) throws SQLException { - return getJdbcValueExtractor().extract( - statement, - startIndex, - session - ); + return getJdbcValueExtractor().extract( statement, startIndex, session ); } @Override public T extract(CallableStatement statement, String paramName, final SharedSessionContractImplementor session) throws SQLException { - return getJdbcValueExtractor().extract( - statement, - paramName, - session - ); + return getJdbcValueExtractor().extract( statement, paramName, session ); } @Override @@ -347,7 +335,8 @@ public void nullSafeSet( Object value, int index, boolean[] settable, - SharedSessionContractImplementor session) throws SQLException { + SharedSessionContractImplementor session) + throws SQLException { } @@ -356,9 +345,8 @@ public void nullSafeSet(CallableStatement st, T value, String name, SharedSessio nullSafeSet( st, value, name, (WrapperOptions) session ); } - @SuppressWarnings("unchecked") protected final void nullSafeSet(CallableStatement st, Object value, String name, WrapperOptions options) throws SQLException { - getJdbcValueBinder().bind( st, (T) value, name, options ); + getJdbcValueBinder().bind( st, javaType.cast( value ) , name, options ); } @Override @@ -379,7 +367,7 @@ public CastType getCastType() { // Due to that, we have to handle some conversions in wrap/unwrap of BooleanJavaType // and the cast type determination here. Note that we interpret the converter in ConvertedBasicTypeImpl // to properly determine the correct cast type - final JdbcType jdbcType = getJdbcType(); + final var jdbcType = getJdbcType(); final int jdbcTypeCode = jdbcType.getDdlTypeCode(); switch ( jdbcTypeCode ) { case Types.BIT: diff --git a/hibernate-core/src/main/java/org/hibernate/type/EntityType.java b/hibernate-core/src/main/java/org/hibernate/type/EntityType.java index f0dcaff0c920..12cc140f8bb9 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/EntityType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/EntityType.java @@ -461,7 +461,7 @@ private Object getUniqueKey(Object value, SharedSessionContractImplementor sessi if ( lazyInitializer != null ) { // If the value is a Proxy and the property access is field, the value returned by // attributeMapping.getAttributeMetadata().getPropertyAccess().getGetter().get( object ) - // is always null except for the id, we need the to use the proxy implementation to + // is always null except for the id, and we need to use the proxy implementation to // extract the property value. value = lazyInitializer.getImplementation(); } @@ -530,7 +530,7 @@ private String loggableString(Object entity, EntityPersister persister) { return associatedEntityName + "#" + entity; } else { - final StringBuilder result = new StringBuilder().append( associatedEntityName ); + final var result = new StringBuilder().append( associatedEntityName ); if ( persister.hasIdentifierProperty() ) { result.append( '#' ).append( identifierString( entity, persister ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractClassJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractClassJavaType.java index 430bb9b04f6e..e84b29138fb7 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractClassJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractClassJavaType.java @@ -81,7 +81,7 @@ public Class getJavaType() { } @Override - public Class getJavaTypeClass() { + public final Class getJavaTypeClass() { return getJavaType(); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java index 15fc1675d5ec..323d9312f228 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java @@ -256,24 +256,21 @@ public X unwrap(T[] value, Class type, WrapperOptions options) { } if ( type.isInstance( value ) ) { - //noinspection unchecked - return (X) value; + return type.cast( value ); } else if ( type == byte[].class ) { - return (X) toBytes( value ); + return type.cast( toBytes( value ) ); } else if ( type == BinaryStream.class ) { - //noinspection unchecked - return (X) new ArrayBackedBinaryStream( toBytes( value ) ); + return type.cast( new ArrayBackedBinaryStream( toBytes( value ) ) ); } else if ( type.isArray() ) { final var preferredJavaTypeClass = type.getComponentType(); - final Object[] unwrapped = (Object[]) newInstance( preferredJavaTypeClass, value.length ); + final var unwrapped = (Object[]) newInstance( preferredJavaTypeClass, value.length ); for ( int i = 0; i < value.length; i++ ) { unwrapped[i] = getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ); } - //noinspection unchecked - return (X) unwrapped; + return type.cast( unwrapped ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigDecimalJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigDecimalJavaType.java index 8accc2bf5555..c04e276c3de9 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigDecimalJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigDecimalJavaType.java @@ -37,6 +37,11 @@ public boolean isInstance(Object value) { return value instanceof BigDecimal; } + @Override + public BigDecimal cast(Object value) { + return (BigDecimal) value; + } + @Override public boolean areEqual(BigDecimal one, BigDecimal another) { return one == another @@ -48,37 +53,36 @@ public int extractHashCode(BigDecimal value) { return value.intValue(); } - @SuppressWarnings("unchecked") public X unwrap(BigDecimal value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( BigDecimal.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( BigInteger.class.isAssignableFrom( type ) ) { - return (X) value.toBigIntegerExact(); + return type.cast( value.toBigIntegerExact() ); } if ( Byte.class.isAssignableFrom( type ) ) { - return (X) Byte.valueOf( value.byteValue() ); + return type.cast( value.byteValue() ); } if ( Short.class.isAssignableFrom( type ) ) { - return (X) Short.valueOf( value.shortValue() ); + return type.cast( value.shortValue() ); } if ( Integer.class.isAssignableFrom( type ) ) { - return (X) Integer.valueOf( value.intValue() ); + return type.cast( value.intValue() ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( value.longValue() ); + return type.cast( value.longValue() ); } if ( Double.class.isAssignableFrom( type ) ) { - return (X) Double.valueOf( value.doubleValue() ); + return type.cast( value.doubleValue() ); } if ( Float.class.isAssignableFrom( type ) ) { - return (X) Float.valueOf( value.floatValue() ); + return type.cast( value.floatValue() ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) value.toString(); + return type.cast( value.toString() ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigIntegerJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigIntegerJavaType.java index 5f9eae0892c6..02cbb5d930b1 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigIntegerJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigIntegerJavaType.java @@ -39,43 +39,47 @@ public boolean isInstance(Object value) { return value instanceof BigInteger; } + @Override + public BigInteger cast(Object value) { + return (BigInteger) value; + } + @Override public int extractHashCode(BigInteger value) { return value.intValue(); } @Override - @SuppressWarnings("unchecked") public X unwrap(BigInteger value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( BigInteger.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( BigDecimal.class.isAssignableFrom( type ) ) { - return (X) new BigDecimal( value ); + return type.cast( new BigDecimal( value ) ); } if ( Byte.class.isAssignableFrom( type ) ) { - return (X) Byte.valueOf( value.byteValue() ); + return type.cast( Byte.valueOf( value.byteValue() ) ); } if ( Short.class.isAssignableFrom( type ) ) { - return (X) Short.valueOf( value.shortValue() ); + return type.cast( value.shortValue() ); } if ( Integer.class.isAssignableFrom( type ) ) { - return (X) Integer.valueOf( value.intValue() ); + return type.cast( value.intValue() ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( value.longValue() ); + return type.cast( value.longValue() ); } if ( Double.class.isAssignableFrom( type ) ) { - return (X) Double.valueOf( value.doubleValue() ); + return type.cast( value.doubleValue() ); } if ( Float.class.isAssignableFrom( type ) ) { - return (X) Float.valueOf( value.floatValue() ); + return type.cast( value.floatValue() ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) value.toString(); + return type.cast( value.toString() ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BlobJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BlobJavaType.java index 22a6c5ed834c..823b664dd31d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BlobJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BlobJavaType.java @@ -70,6 +70,11 @@ public boolean isInstance(Object value) { return value instanceof Blob; } + @Override + public Blob cast(Object value) { + return (Blob) value; + } + @Override public String extractLoggableRepresentation(Blob value) { return value == null ? "null" : "{blob}"; @@ -109,7 +114,6 @@ public Blob getReplacement(Blob original, Blob target, SharedSessionContractImpl } @Override - @SuppressWarnings("unchecked") public X unwrap(Blob value, Class type, WrapperOptions options) { if ( value == null ) { return null; @@ -117,17 +121,17 @@ public X unwrap(Blob value, Class type, WrapperOptions options) { try { if ( Blob.class.isAssignableFrom( type ) ) { - return (X) options.getLobCreator().toJdbcBlob( value ); + return type.cast( options.getLobCreator().toJdbcBlob( value ) ); } else if ( byte[].class.isAssignableFrom( type )) { if (value instanceof BlobImplementer blobImplementer) { // if the incoming Blob is a wrapper, just grab the bytes from its BinaryStream - return (X) blobImplementer.getUnderlyingStream().getBytes(); + return type.cast( blobImplementer.getUnderlyingStream().getBytes() ); } else { try { // otherwise extract the bytes from the stream manually - return (X) value.getBinaryStream().readAllBytes(); + return type.cast( value.getBinaryStream().readAllBytes() ); } catch ( IOException e ) { throw new HibernateException( "IOException occurred reading a binary value", e ); @@ -136,20 +140,20 @@ else if ( byte[].class.isAssignableFrom( type )) { } else if ( BinaryStream.class.isAssignableFrom( type ) ) { if (value instanceof BlobImplementer blobImplementer) { - return (X) blobImplementer.getUnderlyingStream(); + return type.cast( blobImplementer.getUnderlyingStream() ); } else { - return (X) new StreamBackedBinaryStream( value.getBinaryStream(), value.length() ); + return type.cast( new StreamBackedBinaryStream( value.getBinaryStream(), value.length() ) ); } } else if ( InputStream.class.isAssignableFrom( type ) ) { if (value instanceof BlobImplementer blobImplementer) { // if the incoming Blob is a wrapper, just pass along its BinaryStream - return (X) blobImplementer.getUnderlyingStream().getInputStream(); + return type.cast( blobImplementer.getUnderlyingStream().getInputStream() ); } else { // otherwise we need to build a BinaryStream... - return (X) value.getBinaryStream(); + return type.cast( value.getBinaryStream() ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java index 49466879b8a3..3bc1e90b605b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java @@ -10,6 +10,7 @@ import org.hibernate.type.descriptor.java.spi.PrimitiveJavaType; import org.hibernate.type.descriptor.jdbc.JdbcType; +import static java.lang.Boolean.parseBoolean; import static java.lang.Character.toUpperCase; import static org.hibernate.internal.util.CharSequenceHelper.regionMatchesIgnoreCase; @@ -59,7 +60,7 @@ public String toString(Boolean value) { @Override public Boolean fromString(CharSequence string) { - return Boolean.valueOf( string.toString() ); + return parseBoolean( string.toString() ); } @Override @@ -67,6 +68,11 @@ public boolean isInstance(Object value) { return value instanceof Boolean; } + @Override + public Boolean cast(Object value) { + return (Boolean) value; + } + @Override public Boolean fromEncodedString(CharSequence charSequence, int start, int end) { return switch ( charSequence.charAt( start ) ) { @@ -76,32 +82,31 @@ public Boolean fromEncodedString(CharSequence charSequence, int start, int end) }; } - @SuppressWarnings("unchecked") @Override public X unwrap(Boolean value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Boolean.class.isAssignableFrom( type ) || type == Object.class ) { - return (X) value; + return type.cast( value ); } if ( Byte.class.isAssignableFrom( type ) ) { - return (X) toByte( value ); + return type.cast( toByte( value ) ); } if ( Short.class.isAssignableFrom( type ) ) { - return (X) toShort( value ); + return type.cast( toShort( value ) ); } if ( Integer.class.isAssignableFrom( type ) ) { - return (X) toInteger( value ); + return type.cast( toInteger( value ) ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) toLong( value ); + return type.cast( toLong( value ) ); } if ( Character.class.isAssignableFrom( type ) ) { - return (X) Character.valueOf( value ? characterValueTrue : characterValueFalse ); + return type.cast( value ? characterValueTrue : characterValueFalse ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) (value ? stringValueTrue : stringValueFalse); + return type.cast( (value ? stringValueTrue : stringValueFalse) ); } throw unknownUnwrap( type ); } @@ -194,18 +199,18 @@ public int getDefaultSqlScale(Dialect dialect, JdbcType jdbcType) { public String getCheckCondition(String columnName, JdbcType jdbcType, BasicValueConverter converter, Dialect dialect) { if ( converter != null ) { if ( jdbcType.isString() ) { - final Object falseValue = converter.toRelationalValue( false ); - final Object trueValue = converter.toRelationalValue( true ); - final String[] values = getPossibleStringValues( converter, falseValue, trueValue ); - return dialect.getCheckCondition( columnName, values ); + return dialect.getCheckCondition( columnName, + getPossibleStringValues( converter, + converter.toRelationalValue( false ), + converter.toRelationalValue( true ) ) ); } else if ( jdbcType.isInteger() ) { @SuppressWarnings("unchecked") final var numericConverter = (BasicValueConverter) converter; - final Number falseValue = numericConverter.toRelationalValue( false ); - final Number trueValue = numericConverter.toRelationalValue( true ); - final Long[] values = getPossibleNumericValues( numericConverter, falseValue, trueValue ); - return dialect.getCheckCondition( columnName, values ); + return dialect.getCheckCondition( columnName, + getPossibleNumericValues( numericConverter, + numericConverter.toRelationalValue( false ), + numericConverter.toRelationalValue( true ) ) ); } } return null; @@ -221,7 +226,7 @@ private static Long[] getPossibleNumericValues( } catch ( NullPointerException ignored ) { } - final Long[] values = new Long[nullValue != null ? 3 : 2]; + final var values = new Long[nullValue != null ? 3 : 2]; values[0] = falseValue != null ? falseValue.longValue() : null; values[1] = trueValue != null ? trueValue.longValue() : null; if ( nullValue != null ) { @@ -240,7 +245,7 @@ private static String[] getPossibleStringValues( } catch ( NullPointerException ignored ) { } - final String[] values = new String[nullValue != null ? 3 : 2]; + final var values = new String[nullValue != null ? 3 : 2]; values[0] = falseValue != null ? falseValue.toString() : null; values[1] = trueValue != null ? trueValue.toString() : null; if ( nullValue != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java index 182ca0db7c6d..f8e48bdef5df 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java @@ -106,7 +106,7 @@ public X unwrap(boolean[] value, Class type, WrapperOptions options) { } if ( type.isInstance( value ) ) { - return (X) value; + return type.cast( value ); } else if ( Object[].class.isAssignableFrom( type ) ) { final Class preferredJavaTypeClass = type.getComponentType(); @@ -114,16 +114,15 @@ else if ( Object[].class.isAssignableFrom( type ) ) { for ( int i = 0; i < value.length; i++ ) { unwrapped[i] = getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ); } - return (X) unwrapped; + return type.cast( unwrapped ); } else if ( type == byte[].class ) { // byte[] can only be requested if the value should be serialized - return (X) SerializationHelper.serialize( value ); + return type.cast( SerializationHelper.serialize( value ) ); } else if ( type == BinaryStream.class ) { // BinaryStream can only be requested if the value should be serialized - //noinspection unchecked - return (X) new ArrayBackedBinaryStream( SerializationHelper.serialize( value ) ); + return type.cast( new ArrayBackedBinaryStream( SerializationHelper.serialize( value ) ) ); } else if ( type.isArray() ) { final Class preferredJavaTypeClass = type.getComponentType(); @@ -131,7 +130,7 @@ else if ( type.isArray() ) { for ( int i = 0; i < value.length; i++ ) { Array.set( unwrapped, i, getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ) ); } - return (X) unwrapped; + return type.cast( unwrapped ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteArrayJavaType.java index ed0ca91b2e74..ac9efa800946 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteArrayJavaType.java @@ -19,6 +19,9 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import static java.lang.Byte.toUnsignedInt; +import static java.lang.Integer.toHexString; + /** * Descriptor for {@code Byte[]} handling, which disallows {@code null} elements. * This {@link JavaType} is useful if the domain model uses {@code Byte[]} and wants to map to {@link SqlTypes#VARBINARY}. @@ -38,6 +41,11 @@ public boolean isInstance(Object value) { return value instanceof byte[]; } + @Override + public Byte[] cast(Object value) { + return (Byte[]) value; + } + @Override public boolean areEqual(Byte[] one, Byte[] another) { return one == another @@ -57,22 +65,22 @@ public int extractHashCode(Byte[] bytes) { public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { // match legacy behavior final var descriptor = indicators.getJdbcType( indicators.resolveJdbcTypeCode( SqlTypes.VARBINARY ) ); - return descriptor instanceof AdjustableJdbcType - ? ( (AdjustableJdbcType) descriptor ).resolveIndicatedType( indicators, this ) + return descriptor instanceof AdjustableJdbcType adjustableJdbcType + ? adjustableJdbcType.resolveIndicatedType( indicators, this ) : descriptor; } @Override public String toString(Byte[] bytes) { - final StringBuilder buf = new StringBuilder(); + final var string = new StringBuilder(); for ( Byte aByte : bytes ) { - final String hexStr = Integer.toHexString( Byte.toUnsignedInt(aByte) ); + final String hexStr = toHexString( toUnsignedInt( aByte ) ); if ( hexStr.length() == 1 ) { - buf.append( '0' ); + string.append( '0' ); } - buf.append( hexStr ); + string.append( hexStr ); } - return buf.toString(); + return string.toString(); } @Override public Byte[] fromString(CharSequence string) { @@ -90,26 +98,25 @@ public Byte[] fromString(CharSequence string) { return bytes; } - @SuppressWarnings("unchecked") @Override public X unwrap(Byte[] value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Byte[].class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( byte[].class.isAssignableFrom( type ) ) { - return (X) unwrapBytes( value ); + return type.cast( unwrapBytes( value ) ); } if ( InputStream.class.isAssignableFrom( type ) ) { - return (X) new ByteArrayInputStream( unwrapBytes( value ) ); + return type.cast( new ByteArrayInputStream( unwrapBytes( value ) ) ); } if ( BinaryStream.class.isAssignableFrom( type ) ) { - return (X) new ArrayBackedBinaryStream( unwrapBytes( value ) ); + return type.cast( new ArrayBackedBinaryStream( unwrapBytes( value ) ) ); } if ( Blob.class.isAssignableFrom( type ) ) { - return (X) options.getLobCreator().createBlob( unwrapBytes( value ) ); + return type.cast( options.getLobCreator().createBlob( unwrapBytes( value ) ) ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteJavaType.java index 1052f2a58d3f..ad7a4c5387f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteJavaType.java @@ -49,32 +49,36 @@ public boolean isInstance(Object value) { return value instanceof Byte; } - @SuppressWarnings("unchecked") + @Override + public Byte cast(Object value) { + return (Byte) value; + } + @Override public X unwrap(Byte value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Byte.class.isAssignableFrom( type ) || type == Object.class ) { - return (X) value; + return type.cast( value ); } if ( Short.class.isAssignableFrom( type ) ) { - return (X) Short.valueOf( value.shortValue() ); + return type.cast( value.shortValue() ); } if ( Integer.class.isAssignableFrom( type ) ) { - return (X) Integer.valueOf( value.intValue() ); + return type.cast( value.intValue() ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( value.longValue() ); + return type.cast( value.longValue() ); } if ( Double.class.isAssignableFrom( type ) ) { - return (X) Double.valueOf( value.doubleValue() ); + return type.cast( value.doubleValue() ); } if ( Float.class.isAssignableFrom( type ) ) { - return (X) Float.valueOf( value.floatValue() ); + return type.cast( value.floatValue() ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) value.toString(); + return type.cast( value.toString() ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java index 26d916fed2c0..c458b8b0c7c5 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java @@ -88,25 +88,24 @@ public int extractHashCode(Calendar value) { return hashCode; } - @SuppressWarnings("unchecked") public X unwrap(Calendar value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Calendar.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( java.sql.Date.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Date( value.getTimeInMillis() ); + return type.cast( new java.sql.Date( value.getTimeInMillis() ) ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Time( value.getTimeInMillis() % 86_400_000 ); + return type.cast( new java.sql.Time( value.getTimeInMillis() % 86_400_000 ) ); } if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Timestamp( value.getTimeInMillis() ); + return type.cast( new java.sql.Timestamp( value.getTimeInMillis() ) ); } if ( Date.class.isAssignableFrom( type ) ) { - return (X) new Date( value.getTimeInMillis() ); + return type.cast( new Date( value.getTimeInMillis() ) ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java index 517566d06959..10c49e839895 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java @@ -105,25 +105,24 @@ public int extractHashCode(Calendar value) { return hashCode; } - @SuppressWarnings("unchecked") public X unwrap(Calendar value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Calendar.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( java.sql.Date.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Date( value.getTimeInMillis() ); + return type.cast( new java.sql.Date( value.getTimeInMillis() ) ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Time( value.getTimeInMillis() % 86_400_000 ); + return type.cast( new java.sql.Time( value.getTimeInMillis() % 86_400_000 ) ); } if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Timestamp( value.getTimeInMillis() ); + return type.cast( new java.sql.Timestamp( value.getTimeInMillis() ) ); } if ( java.util.Date.class.isAssignableFrom( type ) ) { - return (X) new java.util.Date( value.getTimeInMillis() ); + return type.cast( new java.util.Date( value.getTimeInMillis() ) ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java index bd6a6e91fc30..e544c30f2e2c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java @@ -90,25 +90,24 @@ public int extractHashCode(Calendar value) { return hashCode; } - @SuppressWarnings("unchecked") public X unwrap(Calendar value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Calendar.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( java.sql.Date.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Date( value.getTimeInMillis() ); + return type.cast( new java.sql.Date( value.getTimeInMillis() ) ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Time( value.getTimeInMillis() % 86_400_000 ); + return type.cast( new java.sql.Time( value.getTimeInMillis() % 86_400_000 ) ); } if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Timestamp( value.getTimeInMillis() ); + return type.cast( new java.sql.Timestamp( value.getTimeInMillis() ) ); } if ( Date.class.isAssignableFrom( type ) ) { - return (X) new Date( value.getTimeInMillis() ); + return type.cast( new Date( value.getTimeInMillis() ) ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterArrayJavaType.java index 1f75125b1db2..551ff813ac9b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterArrayJavaType.java @@ -37,6 +37,11 @@ public boolean isInstance(Object value) { return value instanceof Character[]; } + @Override + public Character[] cast(Object value) { + return (Character[]) value; + } + @Override public String toString(Character[] value) { return new String( unwrapChars( value ) ); @@ -71,29 +76,28 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { : descriptor; } - @SuppressWarnings("unchecked") @Override public X unwrap(Character[] value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Character[].class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) new String( unwrapChars( value ) ); + return type.cast( new String( unwrapChars( value ) ) ); } if ( NClob.class.isAssignableFrom( type ) ) { - return (X) options.getLobCreator().createNClob( new String( unwrapChars( value ) ) ); + return type.cast( options.getLobCreator().createNClob( new String( unwrapChars( value ) ) ) ); } if ( Clob.class.isAssignableFrom( type ) ) { - return (X) options.getLobCreator().createClob( new String( unwrapChars( value ) ) ); + return type.cast( options.getLobCreator().createClob( new String( unwrapChars( value ) ) ) ); } if ( Reader.class.isAssignableFrom( type ) ) { - return (X) new StringReader( new String( unwrapChars( value ) ) ); + return type.cast( new StringReader( new String( unwrapChars( value ) ) ) ); } if ( CharacterStream.class.isAssignableFrom( type ) ) { - return (X) new CharacterStreamImpl( new String( unwrapChars( value ) ) ); + return type.cast( new CharacterStreamImpl( new String( unwrapChars( value ) ) ) ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterJavaType.java index fe16e90592cc..817e8fca31bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterJavaType.java @@ -45,20 +45,24 @@ public boolean isInstance(Object value) { return value instanceof Character; } - @SuppressWarnings("unchecked") + @Override + public Character cast(Object value) { + return (Character) value; + } + @Override public X unwrap(Character value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Character.class.isAssignableFrom( type ) || type == Object.class ) { - return (X) value; + return type.cast( value ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) value.toString(); + return type.cast( value.toString() ); } if ( Number.class.isAssignableFrom( type ) ) { - return (X) Short.valueOf( (short) value.charValue() ); + return type.cast( (short) value.charValue() ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ClassJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ClassJavaType.java index 02c0e203681a..90754167e741 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ClassJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ClassJavaType.java @@ -26,6 +26,11 @@ public boolean isInstance(Object value) { return value instanceof Class; } + @Override + public Class cast(Object value) { + return (Class) value; + } + @Override public boolean useObjectEqualsHashCode() { return true; @@ -51,16 +56,15 @@ public Class fromString(CharSequence string) { } @Override - @SuppressWarnings("unchecked") public X unwrap(Class value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Class.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) toString( value ); + return type.cast( toString( value ) ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ClobJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ClobJavaType.java index 398d7ee68491..2ff3bd8fb2ec 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ClobJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ClobJavaType.java @@ -52,6 +52,11 @@ public boolean isInstance(Object value) { return value instanceof Clob; } + @Override + public Clob cast(Object value) { + return (Clob) value; + } + @Override public String extractLoggableRepresentation(Clob value) { return value == null ? "null" : "{clob}"; @@ -81,7 +86,6 @@ public Clob getReplacement(Clob original, Clob target, SharedSessionContractImpl .mergeClob( original, target, session ); } - @SuppressWarnings("unchecked") public X unwrap(final Clob value, Class type, WrapperOptions options) { if ( value == null ) { return null; @@ -89,36 +93,36 @@ public X unwrap(final Clob value, Class type, WrapperOptions options) { try { if ( Clob.class.isAssignableFrom( type ) ) { - return (X) options.getLobCreator().toJdbcClob( value ); + return type.cast( options.getLobCreator().toJdbcClob( value ) ); } else if ( String.class.isAssignableFrom( type ) ) { if (value instanceof ClobImplementer clobImplementer) { // if the incoming Clob is a wrapper, just grab the string from its CharacterStream - return (X) clobImplementer.getUnderlyingStream().asString(); + return type.cast( clobImplementer.getUnderlyingStream().asString() ); } else { // otherwise extract the bytes from the stream manually - return (X) extractString( value.getCharacterStream() ); + return type.cast( extractString( value.getCharacterStream() ) ); } } else if ( Reader.class.isAssignableFrom( type ) ) { if (value instanceof ClobImplementer clobImplementer) { // if the incoming NClob is a wrapper, just pass along its BinaryStream - return (X) clobImplementer.getUnderlyingStream().asReader(); + return type.cast( clobImplementer.getUnderlyingStream().asReader() ); } else { // otherwise we need to build a CharacterStream... - return (X) value.getCharacterStream(); + return type.cast( value.getCharacterStream() ); } } else if ( CharacterStream.class.isAssignableFrom( type ) ) { if (value instanceof ClobImplementer clobImplementer) { // if the incoming Clob is a wrapper, just pass along its CharacterStream - return (X) clobImplementer.getUnderlyingStream(); + return type.cast( clobImplementer.getUnderlyingStream() ); } else { // otherwise we need to build a CharacterStream... - return (X) value.getCharacterStream(); + return type.cast( value.getCharacterStream() ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CurrencyJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CurrencyJavaType.java index a2fa030de8fb..9799b675ea1d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CurrencyJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CurrencyJavaType.java @@ -27,6 +27,11 @@ public boolean isInstance(Object value) { return value instanceof Currency; } + @Override + public Currency cast(Object value) { + return (Currency) value; + } + @Override public boolean useObjectEqualsHashCode() { return true; @@ -42,16 +47,15 @@ public Currency fromString(CharSequence string) { return Currency.getInstance( string.toString() ); } - @SuppressWarnings("unchecked") public X unwrap(Currency value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Currency.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) value.getCurrencyCode(); + return type.cast( value.getCurrencyCode() ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java index 430a8664a5f8..bed568b2643b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java @@ -48,6 +48,11 @@ public boolean isInstance(Object value) { return value instanceof Date; } + @Override + public Date cast(Object value) { + return (Date) value; + } + @Override public TemporalType getPrecision() { return TemporalType.TIMESTAMP; @@ -114,7 +119,6 @@ public int extractHashCode(Date value) { return CalendarJavaType.INSTANCE.extractHashCode( calendar ); } - @SuppressWarnings("unchecked") @Override public X unwrap(Date value, Class type, WrapperOptions options) { if ( value == null ) { @@ -124,30 +128,30 @@ public X unwrap(Date value, Class type, WrapperOptions options) { final java.sql.Date rtn = value instanceof java.sql.Date ? ( java.sql.Date ) value : new java.sql.Date( value.getTime() ); - return (X) rtn; + return type.cast( rtn ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { final java.sql.Time rtn = value instanceof java.sql.Time ? ( java.sql.Time ) value : new java.sql.Time( value.getTime() % 86_400_000 ); - return (X) rtn; + return type.cast( rtn ); } if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { final java.sql.Timestamp rtn = value instanceof Timestamp ? ( java.sql.Timestamp ) value : new java.sql.Timestamp( value.getTime() ); - return (X) rtn; + return type.cast( rtn ); } if ( Date.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( Calendar.class.isAssignableFrom( type ) ) { final GregorianCalendar cal = new GregorianCalendar(); cal.setTimeInMillis( value.getTime() ); - return (X) cal; + return type.cast( cal ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( value.getTime() ); + return type.cast( value.getTime() ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleJavaType.java index 3bf60f6c2497..a4cfcd16d679 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleJavaType.java @@ -53,38 +53,42 @@ public boolean isInstance(Object value) { return value instanceof Double; } - @SuppressWarnings("unchecked") + @Override + public Double cast(Object value) { + return (Double) value; + } + @Override public X unwrap(Double value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Double.class.isAssignableFrom( type ) || type == Object.class ) { - return (X) value; + return type.cast( value ); } if ( Float.class.isAssignableFrom( type ) ) { - return (X) Float.valueOf( value.floatValue() ); + return type.cast( value.floatValue() ); } if ( Byte.class.isAssignableFrom( type ) ) { - return (X) Byte.valueOf( value.byteValue() ); + return type.cast( value.byteValue() ); } if ( Short.class.isAssignableFrom( type ) ) { - return (X) Short.valueOf( value.shortValue() ); + return type.cast( value.shortValue() ); } if ( Integer.class.isAssignableFrom( type ) ) { - return (X) Integer.valueOf( value.intValue() ); + return type.cast( value.intValue() ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( value.longValue() ); + return type.cast( value.longValue() ); } if ( BigInteger.class.isAssignableFrom( type ) ) { - return (X) BigInteger.valueOf( value.longValue() ); + return type.cast( BigInteger.valueOf( value.longValue() ) ); } if ( BigDecimal.class.isAssignableFrom( type ) ) { - return (X) BigDecimal.valueOf( value ); + return type.cast( BigDecimal.valueOf( value ) ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) value.toString(); + return type.cast( value.toString() ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java index 0ed6cc8f997f..10218ee8a963 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java @@ -41,6 +41,11 @@ public boolean isInstance(Object value) { return value instanceof double[]; } + @Override + public double[] cast(Object value) { + return (double[]) value; + } + @Override public String extractLoggableRepresentation(double[] value) { return value == null ? super.extractLoggableRepresentation( null ) : Arrays.toString( value ); @@ -106,7 +111,7 @@ public X unwrap(double[] value, Class type, WrapperOptions options) { } if ( type.isInstance( value ) ) { - return (X) value; + return type.cast( value ); } else if ( Object[].class.isAssignableFrom( type ) ) { final Class preferredJavaTypeClass = type.getComponentType(); @@ -114,16 +119,15 @@ else if ( Object[].class.isAssignableFrom( type ) ) { for ( int i = 0; i < value.length; i++ ) { unwrapped[i] = getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ); } - return (X) unwrapped; + return type.cast( unwrapped ); } else if ( type == byte[].class ) { // byte[] can only be requested if the value should be serialized - return (X) SerializationHelper.serialize( value ); + return type.cast( SerializationHelper.serialize( value ) ); } else if ( type == BinaryStream.class ) { // BinaryStream can only be requested if the value should be serialized - //noinspection unchecked - return (X) new ArrayBackedBinaryStream( SerializationHelper.serialize( value ) ); + return type.cast( new ArrayBackedBinaryStream( SerializationHelper.serialize( value ) ) ); } else if ( type.isArray() ) { final Class preferredJavaTypeClass = type.getComponentType(); @@ -131,7 +135,7 @@ else if ( type.isArray() ) { for ( int i = 0; i < value.length; i++ ) { Array.set( unwrapped, i, getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ) ); } - return (X) unwrapped; + return type.cast( unwrapped ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DurationJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DurationJavaType.java index fa26767f4f6f..5412aa4e194b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DurationJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DurationJavaType.java @@ -48,6 +48,11 @@ public boolean isInstance(Object value) { return value instanceof Duration; } + @Override + public Duration cast(Object value) { + return (Duration) value; + } + @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { return context.getTypeConfiguration() @@ -88,28 +93,27 @@ public Duration fromString(CharSequence string) { } @Override - @SuppressWarnings("unchecked") public X unwrap(Duration duration, Class type, WrapperOptions options) { if ( duration == null ) { return null; } if ( Duration.class.isAssignableFrom( type ) ) { - return (X) duration; + return type.cast( duration ); } if ( BigDecimal.class.isAssignableFrom( type ) ) { - return (X) new BigDecimal( duration.getSeconds() ) + return type.cast( new BigDecimal( duration.getSeconds() ) .movePointRight( 9 ) - .add( new BigDecimal( duration.getNano() ) ); + .add( new BigDecimal( duration.getNano() ) ) ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) duration.toString(); + return type.cast( duration.toString() ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( duration.toNanos() ); + return type.cast( duration.toNanos() ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/EnumJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/EnumJavaType.java index 6f8edb9dfe94..4e33a9875fb8 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/EnumJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/EnumJavaType.java @@ -96,24 +96,23 @@ public T fromString(CharSequence string) { } @Override - @SuppressWarnings("unchecked") public X unwrap(T value, Class type, WrapperOptions options) { if ( String.class.equals( type ) ) { - return (X) toName( value ); + return type.cast( toName( value ) ); } else if ( Long.class.equals( type ) ) { - return (X) toLong( value ); + return type.cast( toLong( value ) ); } else if ( Integer.class.equals( type ) ) { - return (X) toInteger( value ); + return type.cast( toInteger( value ) ); } else if ( Short.class.equals( type ) ) { - return (X) toShort( value ); + return type.cast( toShort( value ) ); } else if ( Byte.class.equals( type ) ) { - return (X) toByte( value ); + return type.cast( toByte( value ) ); } - return (X) value; + return type.cast( value ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatJavaType.java index 4a6c6e9e8f5d..66ed69088b7d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatJavaType.java @@ -52,38 +52,42 @@ public boolean isInstance(Object value) { return value instanceof Float; } - @SuppressWarnings("unchecked") + @Override + public Float cast(Object value) { + return (Float) value; + } + @Override public X unwrap(Float value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Float.class.isAssignableFrom( type ) || type == Object.class ) { - return (X) value; + return type.cast( value ); } if ( Double.class.isAssignableFrom( type ) ) { - return (X) Double.valueOf( value.doubleValue() ); + return type.cast( value.doubleValue() ); } if ( Byte.class.isAssignableFrom( type ) ) { - return (X) Byte.valueOf( value.byteValue() ); + return type.cast( value.byteValue() ); } if ( Short.class.isAssignableFrom( type ) ) { - return (X) Short.valueOf( value.shortValue() ); + return type.cast( value.shortValue() ); } if ( Integer.class.isAssignableFrom( type ) ) { - return (X) Integer.valueOf( value.intValue() ); + return type.cast( value.intValue() ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( value.longValue() ); + return type.cast( value.longValue() ); } if ( BigInteger.class.isAssignableFrom( type ) ) { - return (X) BigInteger.valueOf( value.longValue() ); + return type.cast( BigInteger.valueOf( value.longValue() ) ); } if ( BigDecimal.class.isAssignableFrom( type ) ) { - return (X) BigDecimal.valueOf( value ); + return type.cast( BigDecimal.valueOf( value ) ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) value.toString(); + return type.cast( value.toString() ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java index 25f431b6a114..bd67839519c2 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java @@ -41,6 +41,11 @@ public boolean isInstance(Object value) { return value instanceof float[]; } + @Override + public float[] cast(Object value) { + return (float[]) value; + } + @Override public String extractLoggableRepresentation(float[] value) { return value == null ? super.extractLoggableRepresentation( null ) : Arrays.toString( value ); @@ -106,7 +111,7 @@ public X unwrap(float[] value, Class type, WrapperOptions options) { } if ( type.isInstance( value ) ) { - return (X) value; + return type.cast( value ); } else if ( Object[].class.isAssignableFrom( type ) ) { final Class preferredJavaTypeClass = type.getComponentType(); @@ -114,16 +119,15 @@ else if ( Object[].class.isAssignableFrom( type ) ) { for ( int i = 0; i < value.length; i++ ) { unwrapped[i] = getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ); } - return (X) unwrapped; + return type.cast( unwrapped ); } else if ( type == byte[].class ) { // byte[] can only be requested if the value should be serialized - return (X) SerializationHelper.serialize( value ); + return type.cast( SerializationHelper.serialize( value ) ); } else if ( type == BinaryStream.class ) { // BinaryStream can only be requested if the value should be serialized - //noinspection unchecked - return (X) new ArrayBackedBinaryStream( SerializationHelper.serialize( value ) ); + return type.cast( new ArrayBackedBinaryStream( SerializationHelper.serialize( value ) ) ); } else if ( type.isArray() ) { final Class preferredJavaTypeClass = type.getComponentType(); @@ -131,7 +135,7 @@ else if ( type.isArray() ) { for ( int i = 0; i < value.length; i++ ) { Array.set( unwrapped, i, getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ) ); } - return (X) unwrapped; + return type.cast( unwrapped ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InetAddressJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InetAddressJavaType.java index 1d0d56a15d4c..1d742aa2cfd5 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InetAddressJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InetAddressJavaType.java @@ -51,20 +51,19 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { return indicators.getJdbcType( SqlTypes.INET ); } - @SuppressWarnings("unchecked") @Override public X unwrap(InetAddress value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( InetAddress.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( byte[].class.isAssignableFrom( type ) ) { - return (X) value.getAddress(); + return type.cast( value.getAddress() ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) value.getHostAddress(); + return type.cast( value.getHostAddress() ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaType.java index 5b78c6fa8e6f..7a8b7b46406e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaType.java @@ -47,6 +47,11 @@ public boolean isInstance(Object value) { return value instanceof Instant; } + @Override + public Instant cast(Object value) { + return (Instant) value; + } + @Override public TemporalType getPrecision() { return TemporalType.TIMESTAMP; @@ -88,22 +93,21 @@ public Instant fromString(CharSequence string) { } @Override - @SuppressWarnings("unchecked") public X unwrap(Instant instant, Class type, WrapperOptions options) { if ( instant == null ) { return null; } if ( Instant.class.isAssignableFrom( type ) ) { - return (X) instant; + return type.cast( instant ); } if ( OffsetDateTime.class.isAssignableFrom( type ) ) { - return (X) instant.atOffset( ZoneOffset.UTC ); + return type.cast( instant.atOffset( ZoneOffset.UTC ) ); } if ( Calendar.class.isAssignableFrom( type ) ) { - return (X) GregorianCalendar.from( instant.atZone( ZoneOffset.UTC ) ); + return type.cast( GregorianCalendar.from( instant.atZone( ZoneOffset.UTC ) ) ); } if ( Timestamp.class.isAssignableFrom( type ) ) { @@ -119,27 +123,27 @@ public X unwrap(Instant instant, Class type, WrapperOptions options) { */ final ZonedDateTime zonedDateTime = instant.atZone( ZoneId.systemDefault() ); if ( zonedDateTime.getYear() < 1905 ) { - return (X) Timestamp.valueOf( zonedDateTime.toLocalDateTime() ); + return type.cast( Timestamp.valueOf( zonedDateTime.toLocalDateTime() ) ); } else { - return (X) Timestamp.from( instant ); + return type.cast( Timestamp.from( instant ) ); } } if ( java.sql.Date.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Date( instant.toEpochMilli() ); + return type.cast( new java.sql.Date( instant.toEpochMilli() ) ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return (X) new java.sql.Time( instant.toEpochMilli() % 86_400_000 ); + return type.cast( new java.sql.Time( instant.toEpochMilli() % 86_400_000 ) ); } if ( Date.class.isAssignableFrom( type ) ) { - return (X) Date.from( instant ); + return type.cast( Date.from( instant ) ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( instant.toEpochMilli() ); + return type.cast( instant.toEpochMilli() ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerJavaType.java index cd83479a604b..a38d4827a3d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerJavaType.java @@ -49,38 +49,42 @@ public boolean isInstance(Object value) { return value instanceof Integer; } - @SuppressWarnings("unchecked") + @Override + public Integer cast(Object value) { + return (Integer) value; + } + @Override public X unwrap(Integer value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Integer.class.isAssignableFrom( type ) || type == Object.class ) { - return (X) value; + return type.cast( value ); } if ( Byte.class.isAssignableFrom( type ) ) { - return (X) Byte.valueOf( value.byteValue() ); + return type.cast( value.byteValue() ); } if ( Short.class.isAssignableFrom( type ) ) { - return (X) Short.valueOf( value.shortValue() ); + return type.cast( value.shortValue() ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( value.longValue() ); + return type.cast( value.longValue() ); } if ( Double.class.isAssignableFrom( type ) ) { - return (X) Double.valueOf( value.doubleValue() ); + return type.cast( value.doubleValue() ); } if ( Float.class.isAssignableFrom( type ) ) { - return (X) Float.valueOf( value.floatValue() ); + return type.cast( value.floatValue() ); } if ( BigInteger.class.isAssignableFrom( type ) ) { - return (X) BigInteger.valueOf( value ); + return type.cast( BigInteger.valueOf( value ) ); } if ( BigDecimal.class.isAssignableFrom( type ) ) { - return (X) BigDecimal.valueOf( value ); + return type.cast( BigDecimal.valueOf( value ) ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) value.toString(); + return type.cast( value.toString() ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java index 703e12cf1fbd..5bc19094ea44 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java @@ -41,6 +41,11 @@ public boolean isInstance(Object value) { return value instanceof int[]; } + @Override + public int[] cast(Object value) { + return (int[]) value; + } + @Override public String extractLoggableRepresentation(int[] value) { return value == null ? super.extractLoggableRepresentation( null ) : Arrays.toString( value ); @@ -106,7 +111,7 @@ public X unwrap(int[] value, Class type, WrapperOptions options) { } if ( type.isInstance( value ) ) { - return (X) value; + return type.cast( value ); } else if ( Object[].class.isAssignableFrom( type ) ) { final Class preferredJavaTypeClass = type.getComponentType(); @@ -114,16 +119,15 @@ else if ( Object[].class.isAssignableFrom( type ) ) { for ( int i = 0; i < value.length; i++ ) { unwrapped[i] = getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ); } - return (X) unwrapped; + return type.cast( unwrapped ); } else if ( type == byte[].class ) { // byte[] can only be requested if the value should be serialized - return (X) SerializationHelper.serialize( value ); + return type.cast( SerializationHelper.serialize( value ) ); } else if ( type == BinaryStream.class ) { // BinaryStream can only be requested if the value should be serialized - //noinspection unchecked - return (X) new ArrayBackedBinaryStream( SerializationHelper.serialize( value ) ); + return type.cast( new ArrayBackedBinaryStream( SerializationHelper.serialize( value ) ) ); } else if ( type.isArray() ) { final Class preferredJavaTypeClass = type.getComponentType(); @@ -131,7 +135,7 @@ else if ( type.isArray() ) { for ( int i = 0; i < value.length; i++ ) { Array.set( unwrapped, i, getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ) ); } - return (X) unwrapped; + return type.cast( unwrapped ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java index be7453a46d61..06f7f626be40 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java @@ -105,6 +105,10 @@ default boolean isInstance(Object value) { return getJavaTypeClass().isInstance( value ); } + default T cast(Object value) { + return getJavaTypeClass().cast( value ); + } + /** * Retrieve the {@linkplain MutabilityPlan mutability plan} for this Java type. */ @@ -113,12 +117,11 @@ default MutabilityPlan getMutabilityPlan() { } default T getReplacement(T original, T target, SharedSessionContractImplementor session) { - if ( !getMutabilityPlan().isMutable() || target != null && areEqual( original, target ) ) { - return original; - } - else { - return getMutabilityPlan().deepCopy( original ); - } + final var mutabilityPlan = getMutabilityPlan(); + return !mutabilityPlan.isMutable() + || target != null && areEqual( original, target ) + ? original + : mutabilityPlan.deepCopy( original ); } /** @@ -316,6 +319,25 @@ interface CoercionContext { TypeConfiguration getTypeConfiguration(); } + /** + * Coerce the given value to this type, if possible. + * The default implementation defined her simply + * performs an unchecked cast. Subclasses may override + * to perform meaningful coercion. + * + * @apiNote This operation is currently unsound. It + * should throw {@link CoercionException} when coercion + * fails, or its return value should be changed to + * {@link Object}. However, both of those changes had + * too much impact for now. + * + * @param value The value to coerce + * @param coercionContext The coercion context + * @return The coerced value, or the given value + * if no coercion was possible + * @param The type of the value + */ + @Incubating default T coerce(X value, CoercionContext coercionContext) { //noinspection unchecked return (T) value; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java index 17d55e90340e..bdfb6c23e8ba 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java @@ -74,6 +74,11 @@ public boolean isInstance(Object value) { && !( value instanceof java.sql.Time ); } + @Override + public Date cast(Object value) { + return (Date) value; + } + @Override public boolean areEqual(Date one, Date another) { if ( one == another ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java index f5dc8d21ea55..48bc52f289e2 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java @@ -74,6 +74,11 @@ public boolean isInstance(Object value) { && !( value instanceof java.sql.Date ); } + @Override + public Date cast(Object value) { + return (Date) value; + } + @Override public int extractHashCode(Date value) { final var calendar = Calendar.getInstance(); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java index e18fbc917e1e..2cde56adea3f 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java @@ -75,6 +75,11 @@ public boolean isInstance(Object value) { return value instanceof Date; } + @Override + public Date cast(Object value) { + return (Date) value; + } + @Override public boolean areEqual(Date one, Date another) { if ( one == another ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java index 949d2e4d940d..5d040c8ffee6 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java @@ -43,6 +43,11 @@ public boolean isInstance(Object value) { return value instanceof LocalDate; } + @Override + public LocalDate cast(Object value) { + return (LocalDate) value; + } + @Override public TemporalType getPrecision() { return TemporalType.DATE; @@ -77,18 +82,17 @@ public LocalDate fromString(CharSequence string) { } @Override - @SuppressWarnings("unchecked") public X unwrap(LocalDate value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( LocalDate.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( java.sql.Date.class.isAssignableFrom( type ) ) { - return (X) java.sql.Date.valueOf( value ); + return type.cast( java.sql.Date.valueOf( value ) ); } final LocalDateTime localDateTime = value.atStartOfDay(); @@ -99,23 +103,23 @@ public X unwrap(LocalDate value, Class type, WrapperOptions options) { // but on top of being more complex than the line below, it won't always work. // Timestamp.from() assumes the number of milliseconds since the epoch means the // same thing in Timestamp and Instant, but it doesn't, in particular before 1900. - return (X) Timestamp.valueOf( localDateTime ); + return type.cast( Timestamp.valueOf( localDateTime ) ); } final var zonedDateTime = localDateTime.atZone( ZoneId.systemDefault() ); if ( Calendar.class.isAssignableFrom( type ) ) { - return (X) GregorianCalendar.from( zonedDateTime ); + return type.cast( GregorianCalendar.from( zonedDateTime ) ); } final var instant = zonedDateTime.toInstant(); if ( Date.class.equals( type ) ) { - return (X) Date.from( instant ); + return type.cast( Date.from( instant ) ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( instant.toEpochMilli() ); + return type.cast( instant.toEpochMilli() ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java index 426903e455c3..d9291581c7c8 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java @@ -45,6 +45,11 @@ public boolean isInstance(Object value) { return value instanceof LocalDateTime; } + @Override + public LocalDateTime cast(Object value) { + return (LocalDateTime) value; + } + @Override public TemporalType getPrecision() { return TemporalType.TIMESTAMP; @@ -78,14 +83,13 @@ public LocalDateTime fromString(CharSequence string) { } @Override - @SuppressWarnings("unchecked") public X unwrap(LocalDateTime value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( LocalDateTime.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( Timestamp.class.isAssignableFrom( type ) ) { @@ -94,31 +98,31 @@ public X unwrap(LocalDateTime value, Class type, WrapperOptions options) // but on top of being more complex than the line below, it won't always work. // Timestamp.from() assumes the number of milliseconds since the epoch means the // same thing in Timestamp and Instant, but it doesn't, in particular before 1900. - return (X) Timestamp.valueOf( value ); + return type.cast( Timestamp.valueOf( value ) ); } if ( java.sql.Date.class.isAssignableFrom( type ) ) { final var instant = value.atZone( ZoneId.systemDefault() ).toInstant(); - return (X) java.sql.Date.from( instant ); + return type.cast( java.sql.Date.from( instant ) ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { final var instant = value.atZone( ZoneId.systemDefault() ).toInstant(); - return (X) java.sql.Time.from( instant ); + return type.cast( java.sql.Time.from( instant ) ); } if ( Date.class.isAssignableFrom( type ) ) { final var instant = value.atZone( ZoneId.systemDefault() ).toInstant(); - return (X) Date.from( instant ); + return type.cast( Date.from( instant ) ); } if ( Calendar.class.isAssignableFrom( type ) ) { - return (X) GregorianCalendar.from( value.atZone( ZoneId.systemDefault() ) ); + return type.cast( GregorianCalendar.from( value.atZone( ZoneId.systemDefault() ) ) ); } if ( Long.class.isAssignableFrom( type ) ) { final var instant = value.atZone( ZoneId.systemDefault() ).toInstant(); - return (X) Long.valueOf( instant.toEpochMilli() ); + return type.cast( instant.toEpochMilli() ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java index ac7a498cba6d..160b4c3c0a07 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java @@ -49,6 +49,11 @@ public boolean isInstance(Object value) { return value instanceof LocalTime; } + @Override + public LocalTime cast(Object value) { + return (LocalTime) value; + } + @Override public TemporalType getPrecision() { return TemporalType.TIME; @@ -83,23 +88,22 @@ public LocalTime fromString(CharSequence string) { } @Override - @SuppressWarnings("unchecked") public X unwrap(LocalTime value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( LocalTime.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( Time.class.isAssignableFrom( type ) ) { final var time = Time.valueOf( value ); final int nanos = value.getNano(); return nanos == 0 - ? (X) time + ? type.cast( time ) // Preserve milliseconds, which java.sql.Time supports - : (X) new Time( time.getTime() + roundToPrecision( nanos, 3 ) / 1000000 ); + : type.cast( new Time( time.getTime() + roundToPrecision( nanos, 3 ) / 1000000 ) ); } // Oracle documentation says to set the Date to January 1, 1970 when convert from @@ -111,21 +115,21 @@ public X unwrap(LocalTime value, Class type, WrapperOptions options) { .atZone( ZoneId.systemDefault() ); if ( Calendar.class.isAssignableFrom( type ) ) { - return (X) GregorianCalendar.from( zonedDateTime ); + return type.cast( GregorianCalendar.from( zonedDateTime ) ); } final var instant = zonedDateTime.toInstant(); if ( Timestamp.class.isAssignableFrom( type ) ) { - return (X) Timestamp.from( instant ); + return type.cast( Timestamp.from( instant ) ); } if ( Date.class.equals( type ) ) { - return (X) Date.from( instant ); + return type.cast( Date.from( instant ) ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( instant.toEpochMilli() ); + return type.cast( instant.toEpochMilli() ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocaleJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocaleJavaType.java index 3ab84ac054ca..4222987435b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocaleJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocaleJavaType.java @@ -37,6 +37,11 @@ public boolean isInstance(Object value) { return value instanceof Locale; } + @Override + public Locale cast(Object value) { + return (Locale) value; + } + @Override public boolean useObjectEqualsHashCode() { return true; @@ -181,16 +186,15 @@ private void handleExtension(char[] chars, int start, int length, Locale.Builder } } - @SuppressWarnings("unchecked") public X unwrap(Locale value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Locale.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) (options.useLanguageTagForLocale() ? value.toLanguageTag() : value.toString()); + return type.cast( (options.useLanguageTagForLocale() ? value.toLanguageTag() : value.toString()) ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongJavaType.java index b44b0c871672..f8950f859f69 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongJavaType.java @@ -49,38 +49,42 @@ public boolean isInstance(Object value) { return value instanceof Long; } - @SuppressWarnings("unchecked") + @Override + public Long cast(Object value) { + return (Long) value; + } + @Override public X unwrap(Long value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Long.class.isAssignableFrom( type ) || type == Object.class ) { - return (X) value; + return type.cast( value ); } if ( Byte.class.isAssignableFrom( type ) ) { - return (X) Byte.valueOf( value.byteValue() ); + return type.cast( value.byteValue() ); } if ( Short.class.isAssignableFrom( type ) ) { - return (X) Short.valueOf( value.shortValue() ); + return type.cast( value.shortValue() ); } if ( Integer.class.isAssignableFrom( type ) ) { - return (X) Integer.valueOf( value.intValue() ); + return type.cast( value.intValue() ); } if ( Double.class.isAssignableFrom( type ) ) { - return (X) Double.valueOf( value.doubleValue() ); + return type.cast( value.doubleValue() ); } if ( Float.class.isAssignableFrom( type ) ) { - return (X) Float.valueOf( value.floatValue() ); + return type.cast( value.floatValue() ); } if ( BigInteger.class.isAssignableFrom( type ) ) { - return (X) BigInteger.valueOf( value ); + return type.cast( BigInteger.valueOf( value ) ); } if ( BigDecimal.class.isAssignableFrom( type ) ) { - return (X) BigDecimal.valueOf( value ); + return type.cast( BigDecimal.valueOf( value ) ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) value.toString(); + return type.cast( value.toString() ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java index 9ba2fb465034..9a158499fd82 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java @@ -41,6 +41,11 @@ public boolean isInstance(Object value) { return value instanceof long[]; } + @Override + public long[] cast(Object value) { + return (long[]) value; + } + @Override public String extractLoggableRepresentation(long[] value) { return value == null ? super.extractLoggableRepresentation( null ) : Arrays.toString( value ); @@ -106,7 +111,7 @@ public X unwrap(long[] value, Class type, WrapperOptions options) { } if ( type.isInstance( value ) ) { - return (X) value; + return type.cast( value ); } else if ( Object[].class.isAssignableFrom( type ) ) { final Class preferredJavaTypeClass = type.getComponentType(); @@ -114,16 +119,15 @@ else if ( Object[].class.isAssignableFrom( type ) ) { for ( int i = 0; i < value.length; i++ ) { unwrapped[i] = getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ); } - return (X) unwrapped; + return type.cast( unwrapped ); } else if ( type == byte[].class ) { // byte[] can only be requested if the value should be serialized - return (X) SerializationHelper.serialize( value ); + return type.cast( SerializationHelper.serialize( value ) ); } else if ( type == BinaryStream.class ) { // BinaryStream can only be requested if the value should be serialized - //noinspection unchecked - return (X) new ArrayBackedBinaryStream( SerializationHelper.serialize( value ) ); + return type.cast( new ArrayBackedBinaryStream( SerializationHelper.serialize( value ) ) ); } else if ( type.isArray() ) { final Class preferredJavaTypeClass = type.getComponentType(); @@ -131,7 +135,7 @@ else if ( type.isArray() ) { for ( int i = 0; i < value.length; i++ ) { Array.set( unwrapped, i, getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ) ); } - return (X) unwrapped; + return type.cast( unwrapped ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/MutableMutabilityPlan.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/MutableMutabilityPlan.java index 43505f65a807..ff826c2595ef 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/MutableMutabilityPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/MutableMutabilityPlan.java @@ -15,7 +15,7 @@ */ public abstract class MutableMutabilityPlan implements MutabilityPlan { - public static final MutableMutabilityPlan INSTANCE = new MutableMutabilityPlan() { + public static final MutableMutabilityPlan INSTANCE = new MutableMutabilityPlan<>() { @Override protected Object deepCopyNotNull(Object value) { return value; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/NClobJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/NClobJavaType.java index cfff735f39c5..8b8a5d012036 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/NClobJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/NClobJavaType.java @@ -61,6 +61,11 @@ public boolean isInstance(Object value) { return value instanceof NClob; } + @Override + public NClob cast(Object value) { + return (NClob) value; + } + @Override public String extractLoggableRepresentation(NClob value) { return value == null ? "null" : "{nclob}"; @@ -90,7 +95,6 @@ public NClob getReplacement(NClob original, NClob target, SharedSessionContractI .mergeNClob( original, target, session ); } - @SuppressWarnings("unchecked") public X unwrap(final NClob value, Class type, WrapperOptions options) { if ( value == null ) { return null; @@ -98,36 +102,36 @@ public X unwrap(final NClob value, Class type, WrapperOptions options) { try { if ( NClob.class.isAssignableFrom( type ) ) { - return (X) options.getLobCreator().toJdbcNClob( value ); + return type.cast( options.getLobCreator().toJdbcNClob( value ) ); } else if ( String.class.isAssignableFrom( type ) ) { if (value instanceof NClobImplementer clobImplementer) { // if the incoming Clob is a wrapper, just get the underlying String. - return (X) clobImplementer.getUnderlyingStream().asString(); + return type.cast( clobImplementer.getUnderlyingStream().asString() ); } else { // otherwise we need to extract the String. - return (X) extractString( value.getCharacterStream() ); + return type.cast( extractString( value.getCharacterStream() ) ); } } else if ( Reader.class.isAssignableFrom( type ) ) { if (value instanceof NClobImplementer clobImplementer) { // if the incoming NClob is a wrapper, just pass along its CharacterStream - return (X) clobImplementer.getUnderlyingStream().asReader(); + return type.cast( clobImplementer.getUnderlyingStream().asReader() ); } else { // otherwise we need to build a Reader... - return (X) value.getCharacterStream(); + return type.cast( value.getCharacterStream() ); } } else if ( CharacterStream.class.isAssignableFrom( type ) ) { if (value instanceof NClobImplementer clobImplementer) { // if the incoming NClob is a wrapper, just pass along its CharacterStream - return (X) clobImplementer.getUnderlyingStream(); + return type.cast( clobImplementer.getUnderlyingStream() ); } else { // otherwise we need to build a CharacterStream... - return (X) value.getCharacterStream(); + return type.cast( value.getCharacterStream() ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ObjectArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ObjectArrayJavaType.java index bdf4f8643bdc..12debbcf3249 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ObjectArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ObjectArrayJavaType.java @@ -29,6 +29,11 @@ public boolean isInstance(Object value) { return value instanceof Object[]; } + @Override + public Object[] cast(Object value) { + return (Object[]) value; + } + @Override public String toString(Object[] value) { final StringBuilder sb = new StringBuilder(); @@ -77,14 +82,13 @@ public int extractHashCode(Object[] objects) { return hashCode; } - @SuppressWarnings("unchecked") @Override public X unwrap(Object[] value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Object[].class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ObjectJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ObjectJavaType.java index 885e227a1cad..8daa408337f3 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ObjectJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ObjectJavaType.java @@ -29,10 +29,14 @@ public boolean isInstance(Object value) { return true; } + @Override + public Object cast(Object value) { + return value; + } + @Override public X unwrap(Object value, Class type, WrapperOptions options) { - //noinspection unchecked - return (X) value; + return type.cast( value ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java index 7cbdb112578a..591d57487101 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java @@ -67,6 +67,11 @@ public boolean isInstance(Object value) { return value instanceof OffsetDateTime; } + @Override + public OffsetDateTime cast(Object value) { + return (OffsetDateTime) value; + } + @Override public TemporalType getPrecision() { return TemporalType.TIMESTAMP; @@ -116,26 +121,25 @@ public OffsetDateTime fromEncodedString(CharSequence charSequence, int start, in } @Override - @SuppressWarnings("unchecked") public X unwrap(OffsetDateTime offsetDateTime, Class type, WrapperOptions options) { if ( offsetDateTime == null ) { return null; } if ( OffsetDateTime.class.isAssignableFrom( type ) ) { - return (X) offsetDateTime; + return type.cast( offsetDateTime ); } if ( ZonedDateTime.class.isAssignableFrom( type ) ) { - return (X) offsetDateTime.toZonedDateTime(); + return type.cast( offsetDateTime.toZonedDateTime() ); } if ( Instant.class.isAssignableFrom( type ) ) { - return (X) offsetDateTime.toInstant(); + return type.cast( offsetDateTime.toInstant() ); } if ( Calendar.class.isAssignableFrom( type ) ) { - return (X) GregorianCalendar.from( offsetDateTime.toZonedDateTime() ); + return type.cast( GregorianCalendar.from( offsetDateTime.toZonedDateTime() ) ); } if ( Timestamp.class.isAssignableFrom( type ) ) { @@ -151,30 +155,30 @@ public X unwrap(OffsetDateTime offsetDateTime, Class type, WrapperOptions // - around 1905, both methods are equally valid, so we don't really care which // one is used. if ( offsetDateTime.getYear() < 1905 ) { - return (X) Timestamp.valueOf( + return type.cast( Timestamp.valueOf( offsetDateTime.atZoneSameInstant( ZoneId.systemDefault() ) .toLocalDateTime() - ); + ) ); } else { - return (X) Timestamp.from( offsetDateTime.toInstant() ); + return type.cast( Timestamp.from( offsetDateTime.toInstant() ) ); } } if ( java.sql.Date.class.isAssignableFrom( type ) ) { - return (X) java.sql.Date.from( offsetDateTime.toInstant() ); + return type.cast( java.sql.Date.from( offsetDateTime.toInstant() ) ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return (X) java.sql.Time.from( offsetDateTime.toInstant() ); + return type.cast( java.sql.Time.from( offsetDateTime.toInstant() ) ); } if ( Date.class.isAssignableFrom( type ) ) { - return (X) Date.from( offsetDateTime.toInstant() ); + return type.cast( Date.from( offsetDateTime.toInstant() ) ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( offsetDateTime.toInstant().toEpochMilli() ); + return type.cast( offsetDateTime.toInstant().toEpochMilli() ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java index 0c4ec1a3c9dc..8c58fbf90721 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java @@ -49,6 +49,11 @@ public boolean isInstance(Object value) { return value instanceof OffsetTime; } + @Override + public OffsetTime cast(Object value) { + return (OffsetTime) value; + } + @Override public TemporalType getPrecision() { return TemporalType.TIME; @@ -83,7 +88,6 @@ public OffsetTime fromString(CharSequence string) { } @Override - @SuppressWarnings("unchecked") public X unwrap(OffsetTime offsetTime, Class type, WrapperOptions options) { if ( offsetTime == null ) { return null; @@ -93,15 +97,15 @@ public X unwrap(OffsetTime offsetTime, Class type, WrapperOptions options // (since PS.setObject() doesn't support passing a timezone) if ( OffsetTime.class.isAssignableFrom( type ) ) { - return (X) offsetTime; + return type.cast( offsetTime ); } if ( LocalTime.class.isAssignableFrom( type ) ) { - return (X) offsetTime.withOffsetSameInstant( getCurrentSystemOffset() ).toLocalTime(); + return type.cast( offsetTime.withOffsetSameInstant( getCurrentSystemOffset() ).toLocalTime() ); } if ( OffsetDateTime.class.isAssignableFrom( type ) ) { - return (X) offsetTime.atDate( LocalDate.EPOCH ); + return type.cast( offsetTime.atDate( LocalDate.EPOCH ) ); } // for legacy types, we assume that the JDBC timezone is passed to JDBC @@ -113,9 +117,9 @@ public X unwrap(OffsetTime offsetTime, Class type, WrapperOptions options final var time = Time.valueOf( jdbcOffsetTime.toLocalTime() ); final int nanos = jdbcOffsetTime.getNano(); return nanos == 0 - ? (X) time + ? type.cast( time ) // Preserve milliseconds, which java.sql.Time supports - : (X) new Time( time.getTime() + roundToPrecision( nanos, 3 ) / 1000000 ); + : type.cast( new Time( time.getTime() + roundToPrecision( nanos, 3 ) / 1000000 ) ); } final var jdbcOffsetDateTime = jdbcOffsetTime.atDate( LocalDate.EPOCH ); @@ -126,23 +130,23 @@ public X unwrap(OffsetTime offsetTime, Class type, WrapperOptions options // but this won't always work since Timestamp.from() assumes the number of // milliseconds since the epoch means the same thing in Timestamp and Instant, // but it doesn't, in particular before 1900. - return (X) Timestamp.valueOf( jdbcOffsetDateTime.toLocalDateTime() ); + return type.cast( Timestamp.valueOf( jdbcOffsetDateTime.toLocalDateTime() ) ); } if ( Calendar.class.isAssignableFrom( type ) ) { - return (X) GregorianCalendar.from( jdbcOffsetDateTime.toZonedDateTime() ); + return type.cast( GregorianCalendar.from( jdbcOffsetDateTime.toZonedDateTime() ) ); } // for instants, we assume that the JDBC timezone, if any, is ignored - final Instant instant = offsetTime.atDate( LocalDate.EPOCH ).toInstant(); + final var instant = offsetTime.atDate( LocalDate.EPOCH ).toInstant(); if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( instant.toEpochMilli() ); + return type.cast( instant.toEpochMilli() ); } if ( Date.class.isAssignableFrom( type ) ) { - return (X) Date.from( instant ); + return type.cast( Date.from( instant ) ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java index d68bec284276..f0b51879ce48 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java @@ -37,6 +37,11 @@ public boolean isInstance(Object value) { return value instanceof byte[]; } + @Override + public byte[] cast(Object value) { + return (byte[]) value; + } + @Override public boolean areEqual(byte[] one, byte[] another) { return one == another @@ -99,29 +104,28 @@ public byte[] fromString(CharSequence string) { return bytes; } - @SuppressWarnings("unchecked") public X unwrap(byte[] value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( byte[].class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( InputStream.class.isAssignableFrom( type ) ) { - return (X) new ByteArrayInputStream( value ); + return type.cast( new ByteArrayInputStream( value ) ); } if ( BinaryStream.class.isAssignableFrom( type ) ) { - return (X) new ArrayBackedBinaryStream( value ); + return type.cast( new ArrayBackedBinaryStream( value ) ); } if ( Blob.class.isAssignableFrom( type ) ) { - return (X) options.getLobCreator().createBlob( value ); + return type.cast( options.getLobCreator().createBlob( value ) ); } if ( type.isAssignableFrom( Byte[].class ) ) { final Byte[] array = new Byte[value.length]; for ( int i = 0; i < value.length; i++ ) { array[i] = value[i]; } - return (X) array; + return type.cast( array ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveCharacterArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveCharacterArrayJavaType.java index 33ccf0bcf506..dad8386ce0dd 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveCharacterArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveCharacterArrayJavaType.java @@ -40,6 +40,11 @@ public boolean isInstance(Object value) { return value instanceof char[]; } + @Override + public char[] cast(Object value) { + return (char[]) value; + } + @Override public boolean areEqual(char[] one, char[] another) { return one == another @@ -55,28 +60,27 @@ public int extractHashCode(char[] chars) { return hashCode; } - @SuppressWarnings("unchecked") public X unwrap(char[] value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( char[].class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) new String( value ); + return type.cast( new String( value ) ); } if ( NClob.class.isAssignableFrom( type ) ) { - return (X) options.getLobCreator().createNClob( new String( value ) ); + return type.cast( options.getLobCreator().createNClob( new String( value ) ) ); } if ( Clob.class.isAssignableFrom( type ) ) { - return (X) options.getLobCreator().createClob( new String( value ) ); + return type.cast( options.getLobCreator().createClob( new String( value ) ) ); } if ( Reader.class.isAssignableFrom( type ) ) { - return (X) new StringReader( new String( value ) ); + return type.cast( new StringReader( new String( value ) ) ); } if ( CharacterStream.class.isAssignableFrom( type ) ) { - return (X) new CharacterStreamImpl( new String( value ) ); + return type.cast( new CharacterStreamImpl( new String( value ) ) ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/SerializableJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/SerializableJavaType.java index 58bbd22656bb..39544887a8ec 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/SerializableJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/SerializableJavaType.java @@ -53,10 +53,9 @@ public SerializableJavaType(Class type, MutabilityPlan mutabilityPlan) { } private static MutabilityPlan createMutabilityPlan(Class type) { - if ( type.isAnnotationPresent( Immutable.class ) ) { - return ImmutableMutabilityPlan.instance(); - } - return (MutabilityPlan) SerializableMutabilityPlan.INSTANCE; + return type.isAnnotationPresent( Immutable.class ) + ? ImmutableMutabilityPlan.instance() + : (MutabilityPlan) SerializableMutabilityPlan.INSTANCE; } @Override @@ -97,31 +96,29 @@ public int extractHashCode(T value) { return PrimitiveByteArrayJavaType.INSTANCE.extractHashCode( toBytes( value ) ); } - @SuppressWarnings("unchecked") public X unwrap(T value, Class type, WrapperOptions options) { if ( value == null ) { return null; } else if ( type.isInstance( value ) ) { - return (X) value; + return type.cast( value ); } else if ( byte[].class.isAssignableFrom( type ) ) { - return (X) toBytes( value ); + return type.cast( toBytes( value ) ); } else if ( InputStream.class.isAssignableFrom( type ) ) { - return (X) new ByteArrayInputStream( toBytes( value ) ); + return type.cast( new ByteArrayInputStream( toBytes( value ) ) ); } else if ( BinaryStream.class.isAssignableFrom( type ) ) { - return (X) new ArrayBackedBinaryStream( toBytes( value ) ); + return type.cast( new ArrayBackedBinaryStream( toBytes( value ) ) ); } else if ( Blob.class.isAssignableFrom( type ) ) { - return (X) options.getLobCreator().createBlob( toBytes( value ) ); + return type.cast( options.getLobCreator().createBlob( toBytes( value ) ) ); } throw unknownUnwrap( type ); } - @SuppressWarnings("unchecked") public T wrap(X value, WrapperOptions options) { if ( value == null ) { return null; @@ -141,7 +138,7 @@ else if (value instanceof Blob blob) { } } else if ( getJavaTypeClass().isInstance( value ) ) { - return (T) value; + return cast( value ); } throw unknownWrap( value.getClass() ); } @@ -150,8 +147,7 @@ protected byte[] toBytes(T value) { return SerializationHelper.serialize( value ); } - @SuppressWarnings("unchecked") protected T fromBytes(byte[] bytes) { - return (T) SerializationHelper.deserialize( bytes, getJavaTypeClass().getClassLoader() ); + return cast( SerializationHelper.deserialize( bytes, getJavaTypeClass().getClassLoader() ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortJavaType.java index bfcb09054837..ed6ef2f81e7d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortJavaType.java @@ -48,6 +48,11 @@ public boolean isInstance(Object value) { return value instanceof Short; } + @Override + public Short cast(Object value) { + return (Short) value; + } + @Override public boolean isWider(JavaType javaType) { return switch ( javaType.getTypeName() ) { @@ -56,32 +61,31 @@ public boolean isWider(JavaType javaType) { }; } - @SuppressWarnings("unchecked") @Override public X unwrap(Short value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Short.class.isAssignableFrom( type ) || type == Object.class ) { - return (X) value; + return type.cast( value ); } if ( Byte.class.isAssignableFrom( type ) ) { - return (X) Byte.valueOf( value.byteValue() ); + return type.cast( value.byteValue() ); } if ( Integer.class.isAssignableFrom( type ) ) { - return (X) Integer.valueOf( value.intValue() ); + return type.cast( value.intValue() ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( value.longValue() ); + return type.cast( value.longValue() ); } if ( Double.class.isAssignableFrom( type ) ) { - return (X) Double.valueOf( value.doubleValue() ); + return type.cast( value.doubleValue() ); } if ( Float.class.isAssignableFrom( type ) ) { - return (X) Float.valueOf( value.floatValue() ); + return type.cast( value.floatValue() ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) value.toString(); + return type.cast( value.toString() ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java index 37b7cf758aac..cf0c853419c5 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java @@ -41,6 +41,11 @@ public boolean isInstance(Object value) { return value instanceof short[]; } + @Override + public short[] cast(Object value) { + return (short[]) value; + } + @Override public String extractLoggableRepresentation(short[] value) { return value == null ? super.extractLoggableRepresentation( null ) : Arrays.toString( value ); @@ -106,7 +111,7 @@ public X unwrap(short[] value, Class type, WrapperOptions options) { } if ( type.isInstance( value ) ) { - return (X) value; + return type.cast( value ); } else if ( Object[].class.isAssignableFrom( type ) ) { final Class preferredJavaTypeClass = type.getComponentType(); @@ -114,16 +119,15 @@ else if ( Object[].class.isAssignableFrom( type ) ) { for ( int i = 0; i < value.length; i++ ) { unwrapped[i] = getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ); } - return (X) unwrapped; + return type.cast( unwrapped ); } else if ( type == byte[].class ) { // byte[] can only be requested if the value should be serialized - return (X) SerializationHelper.serialize( value ); + return type.cast( SerializationHelper.serialize( value ) ); } else if ( type == BinaryStream.class ) { // BinaryStream can only be requested if the value should be serialized - //noinspection unchecked - return (X) new ArrayBackedBinaryStream( SerializationHelper.serialize( value ) ); + return type.cast( new ArrayBackedBinaryStream( SerializationHelper.serialize( value ) ) ); } else if ( type.isArray() ) { final Class preferredJavaTypeClass = type.getComponentType(); @@ -131,7 +135,7 @@ else if ( type.isArray() ) { for ( int i = 0; i < value.length; i++ ) { Array.set( unwrapped, i, getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ) ); } - return (X) unwrapped; + return type.cast( unwrapped ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/StringJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/StringJavaType.java index d3ef4969aefd..55bcd5c45b87 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/StringJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/StringJavaType.java @@ -48,6 +48,11 @@ public boolean isInstance(Object value) { return value instanceof String; } + @Override + public String cast(Object value) { + return (String) value; + } + @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators stdIndicators) { final var typeConfiguration = stdIndicators.getTypeConfiguration(); @@ -65,37 +70,36 @@ else if ( stdIndicators.isNationalized() ) { return super.getRecommendedJdbcType( stdIndicators ); } - @SuppressWarnings("unchecked") public X unwrap(String value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( String.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( byte[].class.isAssignableFrom( type ) ) { - return (X) value.getBytes( UTF_8 ); + return type.cast( value.getBytes( UTF_8 ) ); } if ( Reader.class.isAssignableFrom( type ) ) { - return (X) new StringReader( value ); + return type.cast( new StringReader( value ) ); } if ( CharacterStream.class.isAssignableFrom( type ) ) { - return (X) new CharacterStreamImpl( value ); + return type.cast( new CharacterStreamImpl( value ) ); } // Since NClob extends Clob, we need to check if type is an NClob // before checking if type is a Clob. That will ensure that // the correct type is returned. if ( NClob.class.isAssignableFrom( type ) ) { - return (X) options.getLobCreator().createNClob( value ); + return type.cast( options.getLobCreator().createNClob( value ) ); } if ( Clob.class.isAssignableFrom( type ) ) { - return (X) options.getLobCreator().createClob( value ); + return type.cast( options.getLobCreator().createClob( value ) ); } if ( Integer.class.isAssignableFrom( type ) ) { - return (X) (Integer) Integer.parseInt( value ); + return type.cast( Integer.parseInt( value ) ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) (Long) Long.parseLong( value ); + return type.cast( Long.parseLong( value ) ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/TimeZoneJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/TimeZoneJavaType.java index 3dbc3133724f..ca46bab84abe 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/TimeZoneJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/TimeZoneJavaType.java @@ -34,6 +34,11 @@ public boolean isInstance(Object value) { return value instanceof TimeZone; } + @Override + public TimeZone cast(Object value) { + return (TimeZone) value; + } + @Override public boolean useObjectEqualsHashCode() { return true; @@ -47,16 +52,15 @@ public TimeZone fromString(CharSequence string) { return TimeZone.getTimeZone( string.toString() ); } - @SuppressWarnings("unchecked") public X unwrap(TimeZone value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( TimeZone.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) toString( value ); + return type.cast( toString( value ) ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/UUIDJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/UUIDJavaType.java index b8d0462f81b7..49c4c27a7b7d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/UUIDJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/UUIDJavaType.java @@ -32,6 +32,11 @@ public boolean isInstance(Object value) { return value instanceof UUID; } + @Override + public UUID cast(Object value) { + return (UUID) value; + } + @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { return context.getJdbcType( context.getPreferredSqlTypeCodeForUuid() ); @@ -63,19 +68,18 @@ else if ( jdbcType.isBinary() ) { return super.getDefaultSqlLength( dialect, jdbcType ); } - @SuppressWarnings("unchecked") public X unwrap(UUID value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( UUID.class.isAssignableFrom( type ) ) { - return (X) PassThroughTransformer.INSTANCE.transform( value ); + return type.cast( PassThroughTransformer.INSTANCE.transform( value ) ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) ToStringTransformer.INSTANCE.transform( value ); + return type.cast( ToStringTransformer.INSTANCE.transform( value ) ); } if ( byte[].class.isAssignableFrom( type ) ) { - return (X) ToBytesTransformer.INSTANCE.transform( value ); + return type.cast( ToBytesTransformer.INSTANCE.transform( value ) ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/UrlJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/UrlJavaType.java index c111905a754a..2a20d74d9020 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/UrlJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/UrlJavaType.java @@ -29,6 +29,11 @@ public boolean isInstance(Object value) { return value instanceof URL; } + @Override + public URL cast(Object value) { + return (URL) value; + } + @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { return context.getJdbcType( SqlTypes.VARCHAR ); @@ -52,16 +57,15 @@ public URL fromString(CharSequence string) { } } - @SuppressWarnings("unchecked") public X unwrap(URL value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( URL.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) toString( value ); + return type.cast( toString( value ) ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/YearJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/YearJavaType.java index 732d6b910d61..806e8e1094a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/YearJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/YearJavaType.java @@ -28,6 +28,11 @@ public boolean isInstance(Object value) { return value instanceof Year; } + @Override + public Year cast(Object value) { + return (Year) value; + } + @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { return context.getJdbcType( Types.INTEGER ); @@ -48,7 +53,6 @@ public Year fromString(CharSequence string) { return string == null ? null : Year.parse( string, FORMATTER ); } - @SuppressWarnings("unchecked") @Override public X unwrap(Year value, Class type, WrapperOptions options) { if ( value == null ) { @@ -56,19 +60,19 @@ public X unwrap(Year value, Class type, WrapperOptions options) { } if ( type.isInstance( value ) ) { - return (X) value; + return type.cast( value ); } if ( Integer.class.isAssignableFrom( type ) ) { - return (X) Integer.valueOf( value.getValue() ); + return type.cast( value.getValue() ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( value.getValue() ); + return type.cast( (long) value.getValue() ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) toString( value ); + return type.cast( toString( value ) ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneIdJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneIdJavaType.java index da51b52bf2e9..2fba480a7f6d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneIdJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneIdJavaType.java @@ -29,6 +29,11 @@ public boolean isInstance(Object value) { return value instanceof ZoneId; } + @Override + public ZoneId cast(Object value) { + return (ZoneId) value; + } + @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { return indicators.getJdbcType( Types.VARCHAR ); @@ -49,17 +54,16 @@ public ZoneId fromString(CharSequence string) { return string == null ? null : ZoneId.of( string.toString() ); } - @SuppressWarnings("unchecked") @Override public X unwrap(ZoneId value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( ZoneId.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) toString( value ); + return type.cast( toString( value ) ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneOffsetJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneOffsetJavaType.java index ba78dba8a58c..1227e2162e9b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneOffsetJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZoneOffsetJavaType.java @@ -37,6 +37,11 @@ public boolean isInstance(Object value) { return value instanceof ZoneOffset; } + @Override + public ZoneOffset cast(Object value) { + return (ZoneOffset) value; + } + @Override public boolean useObjectEqualsHashCode() { return true; @@ -56,19 +61,18 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { } @Override - @SuppressWarnings("unchecked") public X unwrap(ZoneOffset value, Class type, WrapperOptions wrapperOptions) { if ( value == null ) { return null; } if ( ZoneOffset.class.isAssignableFrom( type ) ) { - return (X) value; + return type.cast( value ); } if ( String.class.isAssignableFrom( type ) ) { - return (X) toString( value ); + return type.cast( toString( value ) ); } if ( Integer.class.isAssignableFrom( type ) ) { - return (X) Integer.valueOf( value.getTotalSeconds() ); + return type.cast( value.getTotalSeconds() ); } throw unknownUnwrap( type ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java index cfd3b950e58d..00e4cbd3166e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java @@ -47,6 +47,11 @@ public boolean isInstance(Object value) { return value instanceof ZonedDateTime; } + @Override + public ZonedDateTime cast(Object value) { + return (ZonedDateTime) value; + } + @Override public TemporalType getPrecision() { return TemporalType.TIMESTAMP; @@ -80,26 +85,25 @@ public ZonedDateTime fromString(CharSequence string) { } @Override - @SuppressWarnings("unchecked") public X unwrap(ZonedDateTime zonedDateTime, Class type, WrapperOptions options) { if ( zonedDateTime == null ) { return null; } if ( ZonedDateTime.class.isAssignableFrom( type ) ) { - return (X) zonedDateTime; + return type.cast( zonedDateTime ); } if ( OffsetDateTime.class.isAssignableFrom( type ) ) { - return (X) OffsetDateTime.of( zonedDateTime.toLocalDateTime(), zonedDateTime.getOffset() ); + return type.cast( OffsetDateTime.of( zonedDateTime.toLocalDateTime(), zonedDateTime.getOffset() ) ); } if ( Instant.class.isAssignableFrom( type ) ) { - return (X) zonedDateTime.toInstant(); + return type.cast( zonedDateTime.toInstant() ); } if ( Calendar.class.isAssignableFrom( type ) ) { - return (X) GregorianCalendar.from( zonedDateTime ); + return type.cast( GregorianCalendar.from( zonedDateTime ) ); } if ( Timestamp.class.isAssignableFrom( type ) ) { @@ -115,30 +119,30 @@ public X unwrap(ZonedDateTime zonedDateTime, Class type, WrapperOptions o // - around 1905, both methods are equally valid, so we don't really care which // one is used. if ( zonedDateTime.getYear() < 1905 ) { - return (X) Timestamp.valueOf( + return type.cast( Timestamp.valueOf( zonedDateTime.withZoneSameInstant( ZoneId.systemDefault() ) .toLocalDateTime() - ); + ) ); } else { - return (X) Timestamp.from( zonedDateTime.toInstant() ); + return type.cast( Timestamp.from( zonedDateTime.toInstant() ) ); } } if ( java.sql.Date.class.isAssignableFrom( type ) ) { - return (X) java.sql.Date.from( zonedDateTime.toInstant() ); + return type.cast( java.sql.Date.from( zonedDateTime.toInstant() ) ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return (X) java.sql.Time.from( zonedDateTime.toInstant() ); + return type.cast( java.sql.Time.from( zonedDateTime.toInstant() ) ); } if ( Date.class.isAssignableFrom( type ) ) { - return (X) Date.from( zonedDateTime.toInstant() ); + return type.cast( Date.from( zonedDateTime.toInstant() ) ); } if ( Long.class.isAssignableFrom( type ) ) { - return (X) Long.valueOf( zonedDateTime.toInstant().toEpochMilli() ); + return type.cast( zonedDateTime.toInstant().toEpochMilli() ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/BasicCollectionJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/BasicCollectionJavaType.java index c0206a382371..fbc44a99a3c1 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/BasicCollectionJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/BasicCollectionJavaType.java @@ -355,21 +355,18 @@ public X unwrap(C value, Class type, WrapperOptions options) { } if ( type.isInstance( value ) ) { - //noinspection unchecked - return (X) value; + return type.cast( value ); } else if ( type == byte[].class ) { // byte[] can only be requested if the value should be serialized - return (X) SerializationHelper.serialize( asArrayList( value ) ); + return type.cast( SerializationHelper.serialize( asArrayList( value ) ) ); } else if ( type == BinaryStream.class ) { // BinaryStream can only be requested if the value should be serialized - //noinspection unchecked - return (X) new ArrayBackedBinaryStream( SerializationHelper.serialize( asArrayList( value ) ) ); + return type.cast( new ArrayBackedBinaryStream( SerializationHelper.serialize( asArrayList( value ) ) ) ); } else if ( type == Object[].class ) { - //noinspection unchecked - return (X) value.toArray(); + return type.cast( value.toArray() ); } else if ( Object[].class.isAssignableFrom( type ) ) { final var preferredJavaTypeClass = type.getComponentType(); @@ -379,13 +376,11 @@ else if ( Object[].class.isAssignableFrom( type ) ) { unwrapped[i] = componentJavaType.unwrap( element, preferredJavaTypeClass, options ); i++; } - //noinspection unchecked - return (X) unwrapped; + return type.cast( unwrapped ); } else if ( type.isArray() ) { final var preferredJavaTypeClass = type.getComponentType(); - //noinspection unchecked - final X unwrapped = (X) newInstance( preferredJavaTypeClass, value.size() ); + final X unwrapped = type.cast( newInstance( preferredJavaTypeClass, value.size() ) ); int i = 0; for ( E element : value ) { set( unwrapped, i, componentJavaType.unwrap( element, preferredJavaTypeClass, options ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/CollectionJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/CollectionJavaType.java index 7eba1547090f..06ae24e597ba 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/CollectionJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/CollectionJavaType.java @@ -6,7 +6,6 @@ import java.io.Serializable; import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; import java.util.Map; import java.util.Objects; @@ -55,12 +54,13 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { public JavaType createJavaType( ParameterizedType parameterizedType, TypeConfiguration typeConfiguration) { - final Type[] typeArguments = parameterizedType.getActualTypeArguments(); + final var typeArguments = parameterizedType.getActualTypeArguments(); final var registry = typeConfiguration.getJavaTypeRegistry(); return switch ( semantics.getCollectionClassification() ) { case ARRAY -> { final var arrayClass = (Class) parameterizedType.getRawType(); - yield (JavaType) new ArrayJavaType<>( registry.resolveDescriptor( arrayClass.getComponentType() ) ); + yield (JavaType) + new ArrayJavaType<>( registry.resolveDescriptor( arrayClass.getComponentType() ) ); } case BAG, ID_BAG, LIST, SET, SORTED_SET, ORDERED_SET -> new BasicCollectionJavaType( diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EmbeddableAggregateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EmbeddableAggregateJavaType.java index 4e9d0d189978..d01abf5b8c24 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EmbeddableAggregateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EmbeddableAggregateJavaType.java @@ -70,8 +70,7 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { @Override public X unwrap(T value, Class type, WrapperOptions options) { if ( type.isAssignableFrom( getJavaTypeClass() ) ) { - //noinspection unchecked - return (X) value; + return type.cast( value ); } throw new UnsupportedOperationException( "Unwrap strategy not known for this Java type: " + getTypeName() @@ -83,13 +82,15 @@ public T wrap(X value, WrapperOptions options) { if ( value == null ) { return null; } - if ( getJavaTypeClass().isInstance( value ) ) { - //noinspection unchecked - return (T) value; + else { + final var javaTypeClass = getJavaTypeClass(); + if ( javaTypeClass.isInstance( value ) ) { + return javaTypeClass.cast( value ); + } + throw new UnsupportedOperationException( + "Wrap strategy not known for this Java type: " + getTypeName() + ); } - throw new UnsupportedOperationException( - "Wrap strategy not known for this Java type: " + getTypeName() - ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java index fa57add4632d..b810d42f178f 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java @@ -58,7 +58,7 @@ public boolean isInstance(Object value) { } } - @Override @SuppressWarnings("unchecked") // safe, we do check + @Override public X unwrap(T value, Class type, WrapperOptions options) { final var id = options.getSessionFactory().getMappingMetamodel() @@ -67,14 +67,15 @@ public X unwrap(T value, Class type, WrapperOptions options) { if ( !type.isInstance( id ) ) { throw new IllegalArgumentException( "Id not an instance of type " + type.getName() ); } - return (X) value; + return type.cast( value ); } - @Override @SuppressWarnings("unchecked") // safe, we do check + @Override public T wrap(X value, WrapperOptions options) { + final var entityClass = getJavaTypeClass(); final var persister = options.getSessionFactory().getMappingMetamodel() - .getEntityDescriptor( getJavaTypeClass() ); + .getEntityDescriptor( entityClass ); final var idType = persister.getIdentifierType().getReturnedClass(); if ( !idType.isInstance( value ) ) { throw new IllegalArgumentException( "Not an instance of id type " + idType.getName() ); @@ -82,7 +83,7 @@ public T wrap(X value, WrapperOptions options) { final var entity = options.getSession() .internalLoad( persister.getEntityName(), value, false, true ); - return (T) entity; + return entityClass.cast( entity ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/FormatMapperBasedJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/FormatMapperBasedJavaType.java index e2dc208d72b7..7f0d208c8bc1 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/FormatMapperBasedJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/FormatMapperBasedJavaType.java @@ -65,13 +65,11 @@ public T fromString(CharSequence string) { @Override public X unwrap(T value, Class type, WrapperOptions options) { if ( type.isAssignableFrom( getJavaTypeClass() ) ) { - //noinspection unchecked - return (X) value; + return type.cast( value ); } else if ( type == String.class ) { - //noinspection unchecked - return (X) getFormatMapper( typeConfiguration ) - .toString( value, this, options ); + return type.cast( getFormatMapper( typeConfiguration ) + .toString( value, this, options ) ); } throw new UnsupportedOperationException( "Unwrap strategy not known for this Java type: " + getTypeName() @@ -80,9 +78,9 @@ else if ( type == String.class ) { @Override public T wrap(X value, WrapperOptions options) { - if ( getJavaTypeClass().isInstance( value ) ) { - //noinspection unchecked - return (T) value; + final var javaTypeClass = getJavaTypeClass(); + if ( javaTypeClass.isInstance( value ) ) { + return javaTypeClass.cast( value ); } else if ( value instanceof String string ) { return getFormatMapper( typeConfiguration ) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/UnknownBasicJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/UnknownBasicJavaType.java index cd8350352248..a4bf77cb7c21 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/UnknownBasicJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/UnknownBasicJavaType.java @@ -64,8 +64,7 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { @Override public X unwrap(T value, Class type, WrapperOptions options) { if ( type.isAssignableFrom( getJavaTypeClass() ) ) { - //noinspection unchecked - return (X) value; + return type.cast( value ); } throw new UnsupportedOperationException( "Unwrap strategy not known for this Java type: " + getTypeName() @@ -74,9 +73,9 @@ public X unwrap(T value, Class type, WrapperOptions options) { @Override public T wrap(X value, WrapperOptions options) { - if ( getJavaTypeClass().isInstance( value ) ) { - //noinspection unchecked - return (T) value; + final var javaTypeClass = getJavaTypeClass(); + if ( javaTypeClass.isInstance( value ) ) { + return javaTypeClass.cast( value ); } throw new UnsupportedOperationException( "Wrap strategy not known for this Java type: " + getTypeName() diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ObjectJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ObjectJdbcType.java index cc7430aeec3d..c7d726f094c0 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ObjectJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ObjectJdbcType.java @@ -70,7 +70,6 @@ protected void doBind(CallableStatement st, X value, String name, WrapperOptions } @Override - @SuppressWarnings("unchecked") public ValueExtractor getExtractor(JavaType javaType) { if ( Serializable.class.isAssignableFrom( javaType.getJavaTypeClass() ) ) { return VarbinaryJdbcType.INSTANCE.getExtractor( javaType ); @@ -79,17 +78,17 @@ public ValueExtractor getExtractor(JavaType javaType) { return new BasicExtractor<>( javaType, this ) { @Override protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException { - return (X) rs.getObject( paramIndex ); + return javaType.cast( rs.getObject( paramIndex ) ); } @Override protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException { - return (X) statement.getObject( index ); + return javaType.cast( statement.getObject( index ) ); } @Override protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException { - return (X) statement.getObject( name ); + return javaType.cast( statement.getObject( name ) ); } }; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/StructHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/StructHelper.java index 445a779203df..f3e56499f8cf 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/StructHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/StructHelper.java @@ -302,8 +302,7 @@ private static void injectCastJdbcValue( Object relationalValue) throws SQLException { assert javaType.isInstance( relationalValue ); - //noinspection unchecked - injectJdbcValue( jdbcValues, jdbcIndex, options, jdbcMapping, javaType, (T) relationalValue ); + injectJdbcValue( jdbcValues, jdbcIndex, options, jdbcMapping, javaType, javaType.cast( relationalValue ) ); } private static void injectJdbcValue( diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/StructJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/StructJdbcType.java index 980a0028c470..59cfcee684f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/StructJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/StructJdbcType.java @@ -208,8 +208,7 @@ private X getValue(Object object, WrapperOptions options) throws SQLException { StructHelper.orderJdbcValues( embeddableMappingType, inverseOrderMapping, jdbcValues.clone(), jdbcValues ); } wrapRawJdbcValues( embeddableMappingType, jdbcValues, 0, options ); - //noinspection unchecked - return (X) jdbcValues; + return javaType.cast( jdbcValues ); } assert embeddableMappingType != null && embeddableMappingType.getJavaType() == getJavaType(); final StructAttributeValues attributeValues = getAttributeValues( @@ -218,8 +217,7 @@ private X getValue(Object object, WrapperOptions options) throws SQLException { jdbcValues, options ); - //noinspection unchecked - return (X) instantiate( embeddableMappingType, attributeValues ); + return javaType.cast( instantiate( embeddableMappingType, attributeValues ) ); } }; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/XmlArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/XmlArrayJdbcType.java index 006718721ca9..ad66399bb35d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/XmlArrayJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/XmlArrayJdbcType.java @@ -64,8 +64,7 @@ protected X fromString(String string, JavaType javaType, WrapperOptions o options.getSession().getJdbcCoordinator().getLogicalConnection().getPhysicalConnection() .createSQLXML(); sqlxml.setString( string ); - //noinspection unchecked - return (X) sqlxml; + return javaType.cast( sqlxml ); } return XmlHelper.arrayFromString( javaType, this, string, options ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/XmlJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/XmlJdbcType.java index ac139bfcaf54..1aa971d38cdd 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/XmlJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/XmlJdbcType.java @@ -94,11 +94,11 @@ protected X fromString(String string, JavaType javaType, WrapperOptions o } if ( javaType.getJavaType() == SQLXML.class ) { final SQLXML sqlxml = - options.getSession().getJdbcCoordinator().getLogicalConnection().getPhysicalConnection() + options.getSession().getJdbcCoordinator() + .getLogicalConnection().getPhysicalConnection() .createSQLXML(); sqlxml.setString( string ); - //noinspection unchecked - return (X) sqlxml; + return javaType.cast( sqlxml ); } return options.getXmlFormatMapper().fromString( string, javaType, options ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/spi/BasicJdbcLiteralFormatter.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/spi/BasicJdbcLiteralFormatter.java index 038e86c56289..c1677f9078fb 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/spi/BasicJdbcLiteralFormatter.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/spi/BasicJdbcLiteralFormatter.java @@ -18,27 +18,24 @@ public BasicJdbcLiteralFormatter(JavaType javaType) { super( javaType ); } - @SuppressWarnings("unchecked") protected X unwrap(Object value, Class unwrapType, WrapperOptions options) { assert value != null; // for performance reasons, avoid conversions if we can if ( unwrapType.isInstance( value ) ) { - return (X) value; + return unwrapType.cast( value ); } else { - final JavaType javaType = getJavaType(); - if ( !javaType.isInstance( value ) ) { - final T coerced = javaType.coerce( value, options::getTypeConfiguration ); - if ( unwrapType.isInstance( coerced ) ) { - return (X) coerced; - } - else { - return javaType.unwrap( coerced, unwrapType, options ); - } + final var javaType = getJavaType(); + if ( javaType.isInstance( value ) ) { + final T castValue = javaType.cast( value ); + return javaType.unwrap( castValue, unwrapType, options ); } else { - return javaType.unwrap( (T) value, unwrapType, options ); + final T coerced = javaType.coerce( value, options::getTypeConfiguration ); + return unwrapType.isInstance( coerced ) + ? unwrapType.cast( coerced ) + : javaType.unwrap( coerced, unwrapType, options ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/internal/CompositeUserTypeJavaTypeWrapper.java b/hibernate-core/src/main/java/org/hibernate/type/internal/CompositeUserTypeJavaTypeWrapper.java index d7454355bbfb..bc61a9cec25d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/internal/CompositeUserTypeJavaTypeWrapper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/internal/CompositeUserTypeJavaTypeWrapper.java @@ -102,15 +102,12 @@ public J fromString(CharSequence string) { @Override public X unwrap(J value, Class type, WrapperOptions options) { assert value == null || userType.returnedClass().isInstance( value ); - - //noinspection unchecked - return (X) value; + return type.cast( value ); } @Override public J wrap(X value, WrapperOptions options) { // assert value == null || userType.returnedClass().isInstance( value ); - //noinspection unchecked return (J) value; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/internal/UserTypeJavaTypeWrapper.java b/hibernate-core/src/main/java/org/hibernate/type/internal/UserTypeJavaTypeWrapper.java index 7bc589fb4fdd..3dcdd99f22bb 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/internal/UserTypeJavaTypeWrapper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/internal/UserTypeJavaTypeWrapper.java @@ -15,7 +15,6 @@ import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.BasicJavaType; import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan; -import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.MutabilityPlan; import org.hibernate.type.descriptor.java.MutabilityPlanExposer; import org.hibernate.type.descriptor.jdbc.JdbcType; @@ -134,22 +133,20 @@ public X unwrap(J value, Class type, WrapperOptions options) { private X unwrap(J value, Class type, BasicValueConverter converter, WrapperOptions options) { if ( value != null && !type.isInstance( value ) && converter != null ) { final Object relationalValue = customType.convertToRelationalValue( value ); - final JavaType relationalJavaType = converter.getRelationalJavaType(); - assert relationalJavaType.isInstance( relationalValue ); - //noinspection unchecked - return relationalJavaType.unwrap( (R) relationalValue, type, options ); + final var relationalJavaType = converter.getRelationalJavaType(); + final var castValue = relationalJavaType.cast( relationalValue ); + return relationalJavaType.unwrap( castValue, type, options ); } else { - //noinspection unchecked - return (X) value; + return type.cast( value ); } } @Override public J wrap(X value, WrapperOptions options) { - final BasicValueConverter converter = customType.getValueConverter(); + final var converter = customType.getValueConverter(); if ( value != null && !userType.returnedClass().isInstance( value ) && converter != null ) { - final JavaType domainJavaType = converter.getDomainJavaType(); + final var domainJavaType = converter.getDomainJavaType(); final Object domainValue = customType.convertToDomainValue( value ); assert domainJavaType.isInstance( value ); return domainJavaType.wrap( domainValue, options ); From 9ed9aeb15f32c5d39336b6eee884b087bbb7e5ca Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 6 Dec 2025 23:45:00 +0100 Subject: [PATCH 038/181] more use of Class.cast() instead of unchecked casts and fallout also a bunch of 'var's --- .../hibernate/boot/internal/MetadataImpl.java | 10 +- .../jaxb/hbm/transform/HbmXmlTransformer.java | 5 +- .../internal/ConverterDescriptors.java | 9 +- .../boot/model/internal/AnnotationHelper.java | 9 +- .../model/internal/NamedGraphCreatorJpa.java | 55 +++--- .../internal/GlobalRegistrationsImpl.java | 3 +- .../BytecodeEnhancementMetadataPojoImpl.java | 6 +- .../bytebuddy/BasicProxyFactoryImpl.java | 50 ++--- .../internal/DefaultCacheKeysFactory.java | 26 ++- .../cache/internal/DisabledCaching.java | 3 +- .../cache/internal/EnabledCaching.java | 5 +- .../AbstractPostgreSQLStructJdbcType.java | 122 +++++-------- .../internal/ConfigurationServiceImpl.java | 51 +++--- .../internal/EntityEntryExtraStateHolder.java | 6 +- .../engine/internal/EntityEntryImpl.java | 6 +- .../internal/ConnectionProviderInitiator.java | 4 +- .../DataSourceConnectionProvider.java | 5 +- .../DriverManagerConnectionProvider.java | 3 +- .../UserSuppliedConnectionProviderImpl.java | 3 +- ...asedMultiTenantConnectionProviderImpl.java | 7 +- ...AbstractMultiTenantConnectionProvider.java | 7 +- .../engine/spi/SessionLazyDelegator.java | 12 +- .../DefaultReplicateEventListener.java | 12 +- .../internal/EventListenerRegistryImpl.java | 5 +- .../org/hibernate/graph/EntityGraphs.java | 4 +- .../hibernate/graph/internal/GraphImpl.java | 2 +- .../org/hibernate/internal/SessionImpl.java | 17 +- .../util/type/PrimitiveWrapperHelper.java | 12 ++ .../EntityManagerFactoryBuilderImpl.java | 4 +- .../org/hibernate/jpa/spi/JpaCompliance.java | 2 +- .../internal/AbstractMultiIdEntityLoader.java | 30 +-- .../internal/IdentifierLoadAccessImpl.java | 5 +- .../metamodel/mapping/ValueMapping.java | 3 +- .../NonAggregatedIdentifierMappingImpl.java | 13 +- .../domain/internal/JpaMetamodelImpl.java | 171 +++++++++--------- .../internal/ProjectionSpecificationImpl.java | 6 +- .../spi/AbstractCommonQueryContract.java | 1 + .../sqm/internal/SqmCriteriaNodeBuilder.java | 75 ++++---- .../sqm/sql/BaseSqmToSqlAstConverter.java | 6 +- .../sqm/tree/domain/AbstractSqmFrom.java | 87 +++++---- .../tree/expression/SqmJsonNullBehavior.java | 7 +- .../tree/select/AbstractSqmSelectQuery.java | 28 +-- .../internal/AbstractJdbcOperationQuery.java | 3 +- .../results/internal/StandardRowReader.java | 98 +++++----- .../sql/results/internal/TupleImpl.java | 23 +-- .../java/org/hibernate/usertype/UserType.java | 5 +- .../java/org/hibernate/orm/test/EnumType.java | 3 +- 47 files changed, 510 insertions(+), 519 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataImpl.java index 22389a3b558a..ec02695b70aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataImpl.java @@ -501,13 +501,13 @@ private void appendListeners( EventType eventType) { final var eventListenerGroup = eventListenerRegistry.getEventListenerGroup( eventType ); for ( String listenerImpl : splitAtCommas( listeners ) ) { - @SuppressWarnings("unchecked") - T listener = (T) instantiate( listenerImpl, classLoaderService ); - if ( !eventType.baseListenerInterface().isInstance( listener ) ) { + final var listener = instantiate( listenerImpl, classLoaderService ); + final var baseListenerInterface = eventType.baseListenerInterface(); + if ( !baseListenerInterface.isInstance( listener ) ) { throw new HibernateException( "Event listener '" + listenerImpl - + "' must implement '" + eventType.baseListenerInterface().getName() + "'"); + + "' must implement '" + baseListenerInterface.getName() + "'"); } - eventListenerGroup.appendListener( listener ); + eventListenerGroup.appendListener( baseListenerInterface.cast( listener ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/HbmXmlTransformer.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/HbmXmlTransformer.java index 559b94e6a50e..94bc743ca848 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/HbmXmlTransformer.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/HbmXmlTransformer.java @@ -713,7 +713,7 @@ private void transferImports() { if ( !hbmImports.isEmpty() ) { final var ormRoot = mappingXmlBinding.getRoot(); for ( var hbmImport : hbmImports ) { - final JaxbHqlImportImpl ormImport = new JaxbHqlImportImpl(); + final var ormImport = new JaxbHqlImportImpl(); ormRoot.getHqlImports().add( ormImport ); ormImport.setClazz( hbmImport.getClazz() ); ormImport.setRename( hbmImport.getRename() ); @@ -890,7 +890,8 @@ private static JaxbNamedHqlQueryImpl transformNamedQuery(JaxbHbmNamedQueryType h query.setQuery( qryString ); } else { - @SuppressWarnings("unchecked") final var element = (JAXBElement) content; + @SuppressWarnings("unchecked") + final var element = (JAXBElement) content; final var hbmQueryParam = element.getValue(); final var queryParam = new JaxbQueryParamTypeImpl(); query.getQueryParam().add( queryParam ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/ConverterDescriptors.java b/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/ConverterDescriptors.java index 047635bd09d5..4c1900dd9274 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/ConverterDescriptors.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/ConverterDescriptors.java @@ -30,8 +30,7 @@ public static ConverterDescriptor of( Class> converterClass, Boolean autoApply, boolean overrideable, ClassmateContext classmateContext) { @SuppressWarnings("unchecked") // work around weird fussiness in wildcard capture - final Class> converterType = - (Class>) converterClass; + final var converterType = (Class>) converterClass; return new ClassBasedConverterDescriptor<>( converterType, autoApply, classmateContext, overrideable ); } @@ -39,8 +38,7 @@ public static ConverterDescriptor of( Class> converterClass, ClassmateContext classmateContext) { @SuppressWarnings("unchecked") // work around weird fussiness in wildcard capture - final Class> converterType = - (Class>) converterClass; + final var converterType = (Class>) converterClass; return new ClassBasedConverterDescriptor<>( converterType, null, classmateContext, false ); } @@ -48,8 +46,7 @@ public static ConverterDescriptor of( Class> converterType, ResolvedType domainTypeToMatch, ResolvedType relationalType, boolean autoApply) { @SuppressWarnings("unchecked") // work around weird fussiness in wildcard capture - final Class> converterClass = - (Class>) converterType; + final var converterClass = (Class>) converterType; return new ConverterDescriptorImpl<>( converterClass, domainTypeToMatch, relationalType, autoApply ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotationHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotationHelper.java index b6cf53a618be..10f300375a5f 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotationHelper.java @@ -41,7 +41,7 @@ public static HashMap extractParameterMap(Parameter[] parameters public static JdbcMapping resolveUserType(Class> userTypeClass, MetadataBuildingContext context) { final var bootstrapContext = context.getBootstrapContext(); - final UserType userType = + final var userType = context.getBuildingOptions().isAllowExtensionsInCdi() ? bootstrapContext.getManagedBeanRegistry().getBean( userTypeClass ).getBeanInstance() : FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( userTypeClass ); @@ -74,7 +74,7 @@ public static JdbcMapping resolveAttributeConverter( public static BasicType resolveBasicType(Class type, MetadataBuildingContext context) { final var typeConfiguration = context.getBootstrapContext().getTypeConfiguration(); - final JavaType jtd = typeConfiguration.getJavaTypeRegistry().findDescriptor( type ); + final var jtd = typeConfiguration.getJavaTypeRegistry().findDescriptor( type ); if ( jtd != null ) { final JdbcType jdbcType = jtd.getRecommendedJdbcType( new JdbcTypeIndicators() { @@ -132,7 +132,7 @@ private static JavaType getJavaType( Class> javaTypeClass, MetadataBuildingContext context, TypeConfiguration typeConfiguration) { - final JavaType registeredJtd = + final var registeredJtd = typeConfiguration.getJavaTypeRegistry() .findDescriptor( javaTypeClass ); if ( registeredJtd != null ) { @@ -143,8 +143,7 @@ else if ( !context.getBuildingOptions().isAllowExtensionsInCdi() ) { } else { return context.getBootstrapContext().getManagedBeanRegistry() - .getBean( javaTypeClass ) - .getBeanInstance(); + .getBean( javaTypeClass ).getBeanInstance(); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorJpa.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorJpa.java index 5e51ff06fcbb..9aebd53de1c2 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorJpa.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorJpa.java @@ -6,7 +6,6 @@ import jakarta.persistence.NamedAttributeNode; import jakarta.persistence.NamedEntityGraph; -import jakarta.persistence.NamedSubgraph; import org.hibernate.AnnotationException; import org.hibernate.boot.model.NamedGraphCreator; import org.hibernate.graph.internal.RootGraphImpl; @@ -41,23 +40,23 @@ public RootGraphImplementor createEntityGraph( Function, EntityDomainType> entityDomainClassResolver, Function> entityDomainNameResolver) { //noinspection unchecked - final EntityDomainType rootEntityType = - (EntityDomainType) entityDomainNameResolver.apply( jpaEntityName ); - final RootGraphImplementor entityGraph = + final var rootEntityType = + (EntityDomainType) + entityDomainNameResolver.apply( jpaEntityName ); + final var entityGraph = createRootGraph( name, rootEntityType, annotation.includeAllAttributes() ); - if ( annotation.subclassSubgraphs() != null ) { - for ( NamedSubgraph subclassSubgraph : annotation.subclassSubgraphs() ) { - final Class subgraphType = subclassSubgraph.type(); - final Class graphJavaType = entityGraph.getGraphedType().getJavaType(); + final var subclassSubgraphs = annotation.subclassSubgraphs(); + if ( subclassSubgraphs != null ) { + for ( var subclassSubgraph : subclassSubgraphs ) { + final var subgraphType = subclassSubgraph.type(); + final var graphJavaType = entityGraph.getGraphedType().getJavaType(); if ( !graphJavaType.isAssignableFrom( subgraphType ) ) { throw new AnnotationException( "Named subgraph type '" + subgraphType.getName() - + "' is not a subtype of the graph type '" + graphJavaType.getName() + "'" ); + + "' is not a subtype of the graph type '" + graphJavaType.getName() + "'" ); } - @SuppressWarnings("unchecked") // Safe, because we just checked - final Class subtype = (Class) subgraphType; - final GraphImplementor subgraph = entityGraph.addTreatedSubgraph( subtype ); - applyNamedAttributeNodes( subclassSubgraph.attributeNodes(), annotation, subgraph ); + applyNamedAttributeNodes( subclassSubgraph.attributeNodes(), annotation, + entityGraph.addTreatedSubgraph( subgraphType.asSubclass( graphJavaType ) ) ); } } @@ -72,7 +71,7 @@ private static RootGraphImplementor createRootGraph( String name, EntityDomainType rootEntityType, boolean includeAllAttributes) { - final RootGraphImpl entityGraph = new RootGraphImpl<>( name, rootEntityType ); + final var entityGraph = new RootGraphImpl<>( name, rootEntityType ); if ( includeAllAttributes ) { for ( var attribute : rootEntityType.getAttributes() ) { entityGraph.addAttributeNodes( attribute ); @@ -85,7 +84,7 @@ private void applyNamedAttributeNodes( NamedAttributeNode[] namedAttributeNodes, NamedEntityGraph namedEntityGraph, GraphImplementor graphNode) { - for ( NamedAttributeNode namedAttributeNode : namedAttributeNodes ) { + for ( var namedAttributeNode : namedAttributeNodes ) { final var attributeNode = (AttributeNodeImplementor) graphNode.addAttributeNode( namedAttributeNode.value() ); @@ -105,7 +104,7 @@ private void applyNamedSubgraphs( String subgraphName, AttributeNodeImplementor attributeNode, boolean isKeySubGraph) { - for ( NamedSubgraph namedSubgraph : namedEntityGraph.subgraphs() ) { + for ( var namedSubgraph : namedEntityGraph.subgraphs() ) { if ( subgraphName.equals( namedSubgraph.name() ) ) { applyNamedAttributeNodes( namedSubgraph.attributeNodes(), namedEntityGraph, createSubgraph( attributeNode, isKeySubGraph, namedSubgraph.type() ) ); @@ -128,27 +127,27 @@ private static SubGraphImplementor createSubgraph( private static SubGraphImplementor makeAttributeNodeValueSubgraph( AttributeNodeImplementor attributeNode, Class subgraphType) { - final Class attributeValueType = - attributeNode.getAttributeDescriptor().getValueGraphType().getJavaType(); + final var attributeValueType = + attributeNode.getAttributeDescriptor() + .getValueGraphType().getJavaType(); if ( !attributeValueType.isAssignableFrom( subgraphType ) ) { throw new AnnotationException( "Named subgraph type '" + subgraphType.getName() - + "' is not a subtype of the value type '" + attributeValueType.getName() + "'" ); + + "' is not a subtype of the value type '" + attributeValueType.getName() + "'" ); } - @SuppressWarnings("unchecked") // Safe, because we just checked - final Class castType = (Class) subgraphType; - return attributeNode.addValueSubgraph().addTreatedSubgraph( castType ); + return attributeNode.addValueSubgraph().addTreatedSubgraph( + subgraphType.asSubclass( attributeNode.getValueSubgraph().getClassType() ) ); } private static SubGraphImplementor makeAttributeNodeKeySubgraph( AttributeNodeImplementor attributeNode, Class subgraphType) { - final Class attributeKeyType = - attributeNode.getAttributeDescriptor().getKeyGraphType().getJavaType(); + final var attributeKeyType = + attributeNode.getAttributeDescriptor() + .getKeyGraphType().getJavaType(); if ( !attributeKeyType.isAssignableFrom( subgraphType ) ) { throw new AnnotationException( "Named subgraph type '" + subgraphType.getName() - + "' is not a subtype of the key type '" + attributeKeyType.getName() + "'" ); + + "' is not a subtype of the key type '" + attributeKeyType.getName() + "'" ); } - @SuppressWarnings("unchecked") // Safe, because we just checked - final Class castType = (Class) subgraphType; - return attributeNode.addKeySubgraph().addTreatedSubgraph( castType ); + return attributeNode.addKeySubgraph().addTreatedSubgraph( + subgraphType.asSubclass( attributeNode.getKeySubgraph().getClassType() ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/internal/GlobalRegistrationsImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/models/internal/GlobalRegistrationsImpl.java index 674c4e1a73ec..bb29a06bef95 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/internal/GlobalRegistrationsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/internal/GlobalRegistrationsImpl.java @@ -157,8 +157,7 @@ public GlobalRegistrationsImpl(ModelsContext sourceModelContext, BootstrapContex @Override public T as(Class type) { - //noinspection unchecked - return (T) this; + return type.cast( this ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeEnhancementMetadataPojoImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeEnhancementMetadataPojoImpl.java index 10677b67feb1..b793a37648f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeEnhancementMetadataPojoImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeEnhancementMetadataPojoImpl.java @@ -272,7 +272,8 @@ public void injectInterceptor( PersistentAttributeInterceptor interceptor, SharedSessionContractImplementor session) { if ( !enhancedForLazyLoading ) { - throw new NotInstrumentedException( "Entity class [" + entityClass.getName() + "] is not enhanced for lazy loading" ); + throw new NotInstrumentedException( "Entity class '" + entityClass.getName() + + "' is not enhanced for lazy loading" ); } if ( !entityClass.isInstance( entity ) ) { @@ -291,7 +292,8 @@ public void injectInterceptor( @Override public @Nullable BytecodeLazyAttributeInterceptor extractLazyInterceptor(Object entity) throws NotInstrumentedException { if ( !enhancedForLazyLoading ) { - throw new NotInstrumentedException( "Entity class [" + entityClass.getName() + "] is not enhanced for lazy loading" ); + throw new NotInstrumentedException( "Entity class [" + entityClass.getName() + + "] is not enhanced for lazy loading" ); } if ( !entityClass.isInstance( entity ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java index 1c86fd450e63..8b0475ec0754 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java @@ -9,7 +9,6 @@ import net.bytebuddy.description.modifier.Visibility; import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; -import org.hibernate.bytecode.enhance.internal.bytebuddy.EnhancerImplConstants; import org.hibernate.bytecode.spi.BasicProxyFactory; import org.hibernate.engine.spi.PrimeAmongSecondarySupertypes; import org.hibernate.internal.util.collections.ArrayHelper; @@ -17,16 +16,17 @@ import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; +import static org.hibernate.proxy.ProxyConfiguration.INTERCEPTOR_FIELD_NAME; + public class BasicProxyFactoryImpl implements BasicProxyFactory { - private static final Class[] NO_INTERFACES = ArrayHelper.EMPTY_CLASS_ARRAY; + private static final Class[] NO_INTERFACES = ArrayHelper.EMPTY_CLASS_ARRAY; private static final String PROXY_NAMING_SUFFIX = "HibernateBasicProxy"; - private final Class proxyClass; - private final Constructor proxyClassConstructor; + private final Class proxyClass; + private final Constructor proxyClassConstructor; - @SuppressWarnings({ "unchecked", "rawtypes" }) - public BasicProxyFactoryImpl(final Class superClass, final Class interfaceClass, final ByteBuddyState byteBuddyState) { + public BasicProxyFactoryImpl(final Class superClass, final Class interfaceClass, final ByteBuddyState byteBuddyState) { if ( superClass == null && interfaceClass == null ) { throw new AssertionFailure( "attempting to build proxy without any superclass or interfaces" ); } @@ -35,9 +35,9 @@ public BasicProxyFactoryImpl(final Class superClass, final Class interfaceClass, throw new AssertionFailure( "Ambiguous call: we assume invocation with EITHER a superClass OR an interfaceClass" ); } - final Class superClassOrMainInterface = superClass != null ? superClass : interfaceClass; - final ByteBuddyState.ProxyDefinitionHelpers helpers = byteBuddyState.getProxyDefinitionHelpers(); - final EnhancerImplConstants constants = byteBuddyState.getEnhancerConstants(); + final var superClassOrMainInterface = superClass != null ? superClass : interfaceClass; + final var helpers = byteBuddyState.getProxyDefinitionHelpers(); + final var constants = byteBuddyState.getEnhancerConstants(); final String proxyClassName = superClassOrMainInterface.getName() + "$" + PROXY_NAMING_SUFFIX; this.proxyClass = byteBuddyState.loadBasicProxy( superClassOrMainInterface, proxyClassName, (byteBuddy, namingStrategy) -> @@ -45,11 +45,11 @@ public BasicProxyFactoryImpl(final Class superClass, final Class interfaceClass, .with( namingStrategy ) .subclass( superClass == null ? Object.class : superClass, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR ) .implement( interfaceClass == null ? NO_INTERFACES : new Class[]{ interfaceClass } ) - .defineField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME, ProxyConfiguration.Interceptor.class, Visibility.PRIVATE ) - .method( byteBuddyState.getProxyDefinitionHelpers().getVirtualNotFinalizerFilter() ) - .intercept( byteBuddyState.getProxyDefinitionHelpers().getDelegateToInterceptorDispatcherMethodDelegation() ) + .defineField( INTERCEPTOR_FIELD_NAME, ProxyConfiguration.Interceptor.class, Visibility.PRIVATE ) + .method( helpers.getVirtualNotFinalizerFilter() ) + .intercept( helpers.getDelegateToInterceptorDispatcherMethodDelegation() ) .implement( constants.INTERFACES_for_ProxyConfiguration ) - .intercept( byteBuddyState.getProxyDefinitionHelpers().getInterceptorFieldAccessor() ) + .intercept( helpers.getInterceptorFieldAccessor() ) ) ); @@ -63,21 +63,25 @@ public BasicProxyFactoryImpl(final Class superClass, final Class interfaceClass, @Override public Object getProxy() { - final PrimeAmongSecondarySupertypes instance; + final var instance = instantiateProxy(); + final var proxyConfiguration = instance.asProxyConfiguration(); + if ( proxyConfiguration == null ) { + throw new HibernateException( "Produced proxy does not correctly implement ProxyConfiguration" ); + } + // Create a dedicated interceptor for the proxy. + // This is required as the interceptor is stateful. + proxyConfiguration.$$_hibernate_set_interceptor( + new PassThroughInterceptor( proxyClass.getName() ) ); + return instance; + } + + private PrimeAmongSecondarySupertypes instantiateProxy() { try { - instance = (PrimeAmongSecondarySupertypes) proxyClassConstructor.newInstance(); + return (PrimeAmongSecondarySupertypes) proxyClassConstructor.newInstance(); } catch (Throwable t) { throw new HibernateException( "Unable to instantiate proxy instance", t ); } - final ProxyConfiguration proxyConfiguration = instance.asProxyConfiguration(); - if ( proxyConfiguration == null ) { - throw new HibernateException( "Produced proxy does not correctly implement ProxyConfiguration" ); - } - // Create a dedicated interceptor for the proxy. This is required as the interceptor is stateful. - final ProxyConfiguration.Interceptor interceptor = new PassThroughInterceptor( proxyClass.getName() ); - proxyConfiguration.$$_hibernate_set_interceptor( interceptor ); - return instance; } public boolean isInstance(Object object) { diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/DefaultCacheKeysFactory.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/DefaultCacheKeysFactory.java index 388812b964de..e9710a217c42 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/DefaultCacheKeysFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/DefaultCacheKeysFactory.java @@ -4,17 +4,17 @@ */ package org.hibernate.cache.internal; -import java.io.Serializable; import org.hibernate.cache.spi.CacheKeysFactory; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.BasicType; import org.hibernate.type.Type; /** - * Second level cache providers now have the option to use custom key implementations. + * Second-level cache providers now have the option to use custom key implementations. * This was done as the default key implementation is very generic and is quite * a large object to allocate in large quantities at runtime. * In some extreme cases, for example when the hit ratio is very low, this was making the efficiency @@ -45,20 +45,30 @@ public class DefaultCacheKeysFactory implements CacheKeysFactory { public static Object staticCreateCollectionKey(Object id, CollectionPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) { final Type keyType = persister.getKeyType(); - final Serializable disassembledKey = keyType.disassemble( id, factory ); + final var coercedId = getCoercedId( id, factory, keyType ); + final var disassembledKey = keyType.disassemble( coercedId, factory ); final boolean idIsArray = disassembledKey.getClass().isArray(); + final String role = persister.getRole(); return tenantIdentifier == null && !idIsArray - ? new BasicCacheKeyImplementation( id, disassembledKey, keyType, persister.getRole() ) - : new CacheKeyImplementation( id, disassembledKey, keyType, persister.getRole(), tenantIdentifier ); + ? new BasicCacheKeyImplementation( coercedId, disassembledKey, keyType, role ) + : new CacheKeyImplementation( coercedId, disassembledKey, keyType, role, tenantIdentifier ); } public static Object staticCreateEntityKey(Object id, EntityPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) { final Type keyType = persister.getIdentifierType(); - final Serializable disassembledKey = keyType.disassemble( id, factory ); + final var coercedId = getCoercedId( id, factory, keyType ); + final var disassembledKey = keyType.disassemble( coercedId, factory ); final boolean idIsArray = disassembledKey.getClass().isArray(); + final String rootEntityName = persister.getRootEntityName(); return tenantIdentifier == null && !idIsArray - ? new BasicCacheKeyImplementation( id, disassembledKey, keyType, persister.getRootEntityName() ) - : new CacheKeyImplementation( id, disassembledKey, keyType, persister.getRootEntityName(), tenantIdentifier ); + ? new BasicCacheKeyImplementation( coercedId, disassembledKey, keyType, rootEntityName ) + : new CacheKeyImplementation( coercedId, disassembledKey, keyType, rootEntityName, tenantIdentifier ); + } + + private static Object getCoercedId(Object id, SessionFactoryImplementor factory, Type keyType) { + return keyType instanceof BasicType basicType + ? basicType.getJavaTypeDescriptor().coerce( id, factory::getTypeConfiguration ) + : id; } public static Object staticCreateNaturalIdKey( diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/DisabledCaching.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/DisabledCaching.java index 8986678a4b13..ade8b582440b 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/DisabledCaching.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/DisabledCaching.java @@ -220,10 +220,9 @@ public void evict(Class cls) { } @Override - @SuppressWarnings("unchecked") public T unwrap(Class type) { if ( type.isAssignableFrom( DisabledCaching.class ) ) { - return (T) this; + return type.cast( this ); } else { throw new PersistenceException( "Hibernate cannot unwrap Cache as '" + type.getName() + "'" ); diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java index 093e2fc606be..76c233fc60cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java @@ -523,13 +523,12 @@ public void evictRegion(String regionName) { } @Override - @SuppressWarnings("unchecked") public T unwrap(Class type) { if ( type.isAssignableFrom( EnabledCaching.class ) ) { - return (T) this; + return type.cast( this ); } else if ( type.isAssignableFrom( RegionFactory.class ) ) { - return (T) regionFactory; + return type.cast( regionFactory ); } else { throw new PersistenceException( "Hibernate cannot unwrap Cache as '" + type.getName() + "'" ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/type/AbstractPostgreSQLStructJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/type/AbstractPostgreSQLStructJdbcType.java index 5a28ee9ef16d..e07a1483242f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/type/AbstractPostgreSQLStructJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/type/AbstractPostgreSQLStructJdbcType.java @@ -4,27 +4,9 @@ */ package org.hibernate.dialect.type; -import java.lang.reflect.Array; -import java.sql.CallableStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeFormatterBuilder; -import java.time.temporal.ChronoField; -import java.time.temporal.TemporalAccessor; -import java.util.ArrayList; -import java.util.TimeZone; - import org.hibernate.internal.util.CharSequenceHelper; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.JdbcMapping; -import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.sql.ast.spi.SqlAppender; @@ -43,13 +25,31 @@ import org.hibernate.type.descriptor.jdbc.StructuredJdbcType; import org.hibernate.type.spi.TypeConfiguration; -import static org.hibernate.type.descriptor.jdbc.StructHelper.getSubPart; -import static org.hibernate.type.descriptor.jdbc.StructHelper.instantiate; +import java.sql.CallableStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.util.ArrayList; +import java.util.TimeZone; + +import static java.lang.reflect.Array.get; +import static java.lang.reflect.Array.getLength; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis; +import static org.hibernate.type.descriptor.jdbc.StructHelper.getSubPart; +import static org.hibernate.type.descriptor.jdbc.StructHelper.instantiate; /** * Implementation for serializing/deserializing an embeddable aggregate to/from the PostgreSQL component format. @@ -189,15 +189,13 @@ protected X fromString(String string, JavaType javaType, WrapperOptions o } assert end == string.length(); if ( returnEmbeddable ) { - final StructAttributeValues attributeValues = getAttributeValues( embeddableMappingType, orderMapping, array, options ); - //noinspection unchecked - return (X) instantiate( embeddableMappingType, attributeValues ); + final var attributeValues = getAttributeValues( embeddableMappingType, orderMapping, array, options ); + return javaType.cast( instantiate( embeddableMappingType, attributeValues ) ); } else if ( inverseOrderMapping != null ) { StructHelper.orderJdbcValues( embeddableMappingType, inverseOrderMapping, array.clone(), array ); } - //noinspection unchecked - return (X) array; + return javaType.cast( array ); } private int deserializeStruct( @@ -336,7 +334,7 @@ private int deserializeStruct( continue; } assert isDoubleQuote( string, i, 1 << quotes ); - final JdbcMapping jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); + final var jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) { case SqlTypes.DATE: values[column] = fromRawObject( @@ -450,7 +448,7 @@ private int deserializeStruct( i += expectedQuotes - 1; if ( string.charAt( i + 1 ) == '(' ) { // This could be a nested struct - final JdbcMapping jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); + final var jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); if ( jdbcMapping.getJdbcType() instanceof AbstractPostgreSQLStructJdbcType structJdbcType ) { final Object[] subValues = new Object[structJdbcType.embeddableMappingType.getJdbcValueCount()]; final int subEnd = structJdbcType.deserializeStruct( @@ -500,7 +498,7 @@ private int deserializeStruct( } else if ( string.charAt( i + 1 ) == '{' ) { // This could be a quoted array - final JdbcMapping jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); + final var jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); if ( jdbcMapping instanceof BasicPluralType pluralType ) { final ArrayList arrayList = new ArrayList<>(); //noinspection unchecked @@ -543,7 +541,7 @@ else if ( string.charAt( i + 1 ) == '{' ) { values[column] = null; } else { - final JdbcMapping jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); + final var jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); if ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() == SqlTypes.BOOLEAN ) { values[column] = fromRawObject( jdbcMapping, @@ -579,7 +577,7 @@ else if ( jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass().isEnum() values[column] = null; } else { - final JdbcMapping jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); + final var jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); if ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() == SqlTypes.BOOLEAN ) { values[column] = fromRawObject( jdbcMapping, @@ -610,7 +608,7 @@ else if ( jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass().isEnum() break; case '{': if ( !inQuote ) { - final BasicPluralType pluralType = (BasicPluralType) getJdbcValueSelectable( column ).getJdbcMapping(); + final var pluralType = (BasicPluralType) getJdbcValueSelectable( column ).getJdbcMapping(); final ArrayList arrayList = new ArrayList<>(); //noinspection unchecked i = deserializeArray( @@ -645,14 +643,10 @@ private boolean isBinary(int column) { } private static boolean isBinary(JdbcMapping jdbcMapping) { - switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) { - case SqlTypes.BINARY: - case SqlTypes.VARBINARY: - case SqlTypes.LONGVARBINARY: - case SqlTypes.LONG32VARBINARY: - return true; - } - return false; + return switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) { + case SqlTypes.BINARY, SqlTypes.VARBINARY, SqlTypes.LONGVARBINARY, SqlTypes.LONG32VARBINARY -> true; + default -> false; + }; } private int deserializeArray( @@ -803,7 +797,7 @@ private int deserializeArray( ); break; default: - if ( escapingSb == null || escapingSb.length() == 0 ) { + if ( escapingSb == null || escapingSb.isEmpty() ) { values.add( fromString( elementType, @@ -1027,19 +1021,6 @@ private static boolean isDoubleQuote(String string, int start, int escapes) { return false; } - private Object fromString( - int selectableIndex, - String string, - int start, - int end) { - return fromString( - getJdbcValueSelectable( selectableIndex ).getJdbcMapping(), - string, - start, - end - ); - } - private static Object fromString(JdbcMapping jdbcMapping, CharSequence charSequence, int start, int end) { return jdbcMapping.getJdbcJavaType().fromEncodedString( charSequence, @@ -1064,22 +1045,19 @@ private Object parseTime(CharSequence subSequence) { } private Object parseTimestamp(CharSequence subSequence, JavaType jdbcJavaType) { - final TemporalAccessor temporalAccessor = LOCAL_DATE_TIME.parse( subSequence ); - final LocalDateTime localDateTime = LocalDateTime.from( temporalAccessor ); - final Timestamp timestamp = Timestamp.valueOf( localDateTime ); + final var temporalAccessor = LOCAL_DATE_TIME.parse( subSequence ); + final var localDateTime = LocalDateTime.from( temporalAccessor ); + final var timestamp = Timestamp.valueOf( localDateTime ); timestamp.setNanos( temporalAccessor.get( ChronoField.NANO_OF_SECOND ) ); return timestamp; } private Object parseTimestampWithTimeZone(CharSequence subSequence, JavaType jdbcJavaType) { - final TemporalAccessor temporalAccessor = LOCAL_DATE_TIME.parse( subSequence ); + final var temporalAccessor = LOCAL_DATE_TIME.parse( subSequence ); if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { - if ( jdbcJavaType.getJavaTypeClass() == Instant.class ) { - return Instant.from( temporalAccessor ); - } - else { - return OffsetDateTime.from( temporalAccessor ); - } + return jdbcJavaType.getJavaTypeClass() == Instant.class + ? Instant.from( temporalAccessor ) + : OffsetDateTime.from( temporalAccessor ); } return LocalDateTime.from( temporalAccessor ); } @@ -1129,7 +1107,7 @@ protected String toString(X value, JavaType javaType, WrapperOptions opti if ( value == null ) { return null; } - final StringBuilder sb = new StringBuilder(); + final var sb = new StringBuilder(); serializeStructTo( new PostgreSQLAppender( sb ), value, options ); return sb.toString(); } @@ -1164,10 +1142,10 @@ private void serializeJdbcValuesTo( if ( jdbcValue == null ) { continue; } - final SelectableMapping selectableMapping = orderMapping == null ? + final var selectableMapping = orderMapping == null ? embeddableMappingType.getJdbcValueSelectable( i ) : embeddableMappingType.getJdbcValueSelectable( orderMapping[i] ); - final JdbcMapping jdbcMapping = selectableMapping.getJdbcMapping(); + final var jdbcMapping = selectableMapping.getJdbcMapping(); if ( jdbcMapping.getJdbcType() instanceof AbstractPostgreSQLStructJdbcType structJdbcType ) { appender.quoteStart(); structJdbcType.serializeJdbcValuesTo( @@ -1267,7 +1245,7 @@ private void serializeConvertedBasicTo( break; case SqlTypes.ARRAY: if ( subValue != null ) { - final int length = Array.getLength( subValue ); + final int length = getLength( subValue ); if ( length == 0 ) { appender.append( "{}" ); } @@ -1276,7 +1254,7 @@ private void serializeConvertedBasicTo( final BasicType elementType = ((BasicPluralType) jdbcMapping).getElementType(); appender.quoteStart(); appender.append( '{' ); - Object arrayElement = Array.get( subValue, 0 ); + Object arrayElement = get( subValue, 0 ); if ( arrayElement == null ) { appender.appendNull(); } @@ -1284,7 +1262,7 @@ private void serializeConvertedBasicTo( serializeConvertedBasicTo( appender, options, elementType, arrayElement ); } for ( int i = 1; i < length; i++ ) { - arrayElement = Array.get( subValue, i ); + arrayElement = get( subValue, i ); appender.append( ',' ); if ( arrayElement == null ) { appender.appendNull(); @@ -1301,7 +1279,7 @@ private void serializeConvertedBasicTo( break; case SqlTypes.STRUCT: if ( subValue != null ) { - final AbstractPostgreSQLStructJdbcType structJdbcType = (AbstractPostgreSQLStructJdbcType) jdbcMapping.getJdbcType(); + final var structJdbcType = (AbstractPostgreSQLStructJdbcType) jdbcMapping.getJdbcType(); appender.quoteStart(); structJdbcType.serializeJdbcValuesTo( appender, options, (Object[]) subValue, '(' ); appender.append( ')' ); @@ -1354,7 +1332,7 @@ private int injectAttributeValue( Object[] rawJdbcValues, int jdbcIndex, WrapperOptions options) throws SQLException { - final MappingType mappedType = modelPart.getMappedType(); + final var mappedType = modelPart.getMappedType(); final int jdbcValueCount; final Object rawJdbcValue = rawJdbcValues[jdbcIndex]; if ( mappedType instanceof EmbeddableMappingType embeddableMappingType ) { @@ -1378,7 +1356,7 @@ private int injectAttributeValue( else { assert modelPart.getJdbcTypeCount() == 1; jdbcValueCount = 1; - final JdbcMapping jdbcMapping = modelPart.getSingleJdbcMapping(); + final var jdbcMapping = modelPart.getSingleJdbcMapping(); final Object jdbcValue = jdbcMapping.getJdbcJavaType().wrap( rawJdbcValue, options diff --git a/hibernate-core/src/main/java/org/hibernate/engine/config/internal/ConfigurationServiceImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/config/internal/ConfigurationServiceImpl.java index 005ee176c523..ed0ac5d4fecc 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/config/internal/ConfigurationServiceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/config/internal/ConfigurationServiceImpl.java @@ -66,42 +66,49 @@ public void injectServices(ServiceRegistryImplementor serviceRegistry) { return target !=null ? target : defaultValue; } - @SuppressWarnings("unchecked") public @Nullable T cast(Class expected, @Nullable Object candidate){ - if (candidate == null) { + if ( candidate == null ) { return null; } + else if ( expected.isInstance( candidate ) ) { + return expected.cast( candidate ); + } + else { + final var target = getTargetClass( expected, candidate ); + return target == null ? null : instantiate( expected, target ); - if ( expected.isInstance( candidate ) ) { - return (T) candidate; } + } + + private static @Nullable T instantiate(Class expected, Class target) { + try { + return target.getDeclaredConstructor().newInstance(); + } + catch (Exception e) { + //TODO: should this really be a debug-level + // log instead of a proper error? + CORE_LOGGER.debugf( "Unable to instantiate %s class %s", + expected.getName(), target.getName() ); + return null; + } + } - Class target; - if (candidate instanceof Class) { - target = (Class) candidate; + private @Nullable Class getTargetClass(Class expected, Object candidate) { + if ( candidate instanceof Class candidateClass ) { + return candidateClass.asSubclass( expected ); } else { try { - target = serviceRegistry.requireService( ClassLoaderService.class ) + return serviceRegistry.requireService( ClassLoaderService.class ) .classForName( candidate.toString() ); } - catch ( ClassLoadingException e ) { + catch (ClassLoadingException e) { + //TODO: should this really be a debug-level + // log instead of a proper error? CORE_LOGGER.debugf( "Unable to locate %s implementation class %s", expected.getName(), candidate.toString() ); - target = null; + return null; } } - if ( target != null ) { - try { - return target.newInstance(); - } - catch ( Exception e ) { - CORE_LOGGER.debugf( "Unable to instantiate %s class %s", - expected.getName(), target.getName() ); - } - } - return null; } - - } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryExtraStateHolder.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryExtraStateHolder.java index 6a53f3f79b0d..95ba26e31fff 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryExtraStateHolder.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryExtraStateHolder.java @@ -36,13 +36,13 @@ public void addExtraState(EntityEntryExtraState extraState) { } } - @Override @SuppressWarnings("unchecked") + @Override public T getExtraState(Class extraStateType) { if ( next == null ) { return null; } - if ( extraStateType.isAssignableFrom( next.getClass() ) ) { - return (T) next; + if ( extraStateType.isInstance( next ) ) { + return extraStateType.cast( next ); } else { return next.getExtraState( extraStateType ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryImpl.java index f32adf748e9b..37fce72bf597 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryImpl.java @@ -548,13 +548,13 @@ public void addExtraState(EntityEntryExtraState extraState) { } } - @Override @SuppressWarnings("unchecked") + @Override public T getExtraState(Class extraStateType) { if ( next == null ) { return null; } - else if ( extraStateType.isAssignableFrom( next.getClass() ) ) { - return (T) next; + else if ( extraStateType.isInstance( next ) ) { + return extraStateType.cast( next ); } else { return next.getExtraState( extraStateType ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionProviderInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionProviderInitiator.java index e39abf7d9278..ef0112e9ada2 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionProviderInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionProviderInitiator.java @@ -116,9 +116,7 @@ private static Class connectionProviderClass(Class throw new ConnectionProviderConfigurationException( "Class '" + providerClass.getName() + "' does not implement 'ConnectionProvider'" ); } - @SuppressWarnings("unchecked") // Safe, we just checked - final var connectionProviderClass = (Class) providerClass; - return connectionProviderClass; + return providerClass.asSubclass( ConnectionProvider.class ); } private ConnectionProvider instantiateNamedConnectionProvider( diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DataSourceConnectionProvider.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DataSourceConnectionProvider.java index 5fb8e27e552e..3d1d60479286 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DataSourceConnectionProvider.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DataSourceConnectionProvider.java @@ -73,13 +73,12 @@ public boolean isUnwrappableAs(Class unwrapType) { } @Override - @SuppressWarnings("unchecked") public T unwrap(Class unwrapType) { if ( unwrapType.isAssignableFrom( DataSourceConnectionProvider.class ) ) { - return (T) this; + return unwrapType.cast( this ); } else if ( unwrapType.isAssignableFrom( DataSource.class) ) { - return (T) getDataSource(); + return unwrapType.cast( getDataSource() ); } else { throw new UnknownUnwrapTypeException( unwrapType ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProvider.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProvider.java index 3013dd5054d0..600294069c26 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProvider.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProvider.java @@ -322,10 +322,9 @@ public boolean isUnwrappableAs(Class unwrapType) { } @Override - @SuppressWarnings( {"unchecked"}) public T unwrap(Class unwrapType) { if ( unwrapType.isAssignableFrom( DriverManagerConnectionProvider.class ) ) { - return (T) this; + return unwrapType.cast( this ); } else { throw new UnknownUnwrapTypeException( unwrapType ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/UserSuppliedConnectionProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/UserSuppliedConnectionProviderImpl.java index f85a9144faac..35c79503eba4 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/UserSuppliedConnectionProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/UserSuppliedConnectionProviderImpl.java @@ -25,10 +25,9 @@ public boolean isUnwrappableAs(Class unwrapType) { } @Override - @SuppressWarnings( {"unchecked"}) public T unwrap(Class unwrapType) { if ( unwrapType.isAssignableFrom( UserSuppliedConnectionProviderImpl.class ) ) { - return (T) this; + return unwrapType.cast( this ); } else { throw new UnknownUnwrapTypeException( unwrapType ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractDataSourceBasedMultiTenantConnectionProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractDataSourceBasedMultiTenantConnectionProviderImpl.java index e14bafb1729b..0649321fd924 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractDataSourceBasedMultiTenantConnectionProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractDataSourceBasedMultiTenantConnectionProviderImpl.java @@ -52,13 +52,12 @@ public boolean isUnwrappableAs(Class unwrapType) { } @Override - @SuppressWarnings("unchecked") - public T unwrap(Class unwrapType) { + public X unwrap(Class unwrapType) { if ( unwrapType.isInstance( this ) ) { - return (T) this; + return unwrapType.cast( this ); } else if ( unwrapType.isAssignableFrom( DataSource.class ) ) { - return (T) selectAnyDataSource(); + return unwrapType.cast( selectAnyDataSource() ); } else { throw new UnknownUnwrapTypeException( unwrapType ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractMultiTenantConnectionProvider.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractMultiTenantConnectionProvider.java index 6117ea3e59d0..55e98ec0b8d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractMultiTenantConnectionProvider.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/AbstractMultiTenantConnectionProvider.java @@ -54,13 +54,12 @@ public boolean isUnwrappableAs(Class unwrapType) { } @Override - @SuppressWarnings("unchecked") - public T unwrap(Class unwrapType) { + public X unwrap(Class unwrapType) { if ( unwrapType.isInstance( this ) ) { - return (T) this; + return unwrapType.cast( this ); } else if ( unwrapType.isAssignableFrom( ConnectionProvider.class ) ) { - return (T) getAnyConnectionProvider(); + return unwrapType.cast( getAnyConnectionProvider() ); } else { throw new UnknownUnwrapTypeException( unwrapType ); 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 19fbf55a7a03..ca102676a444 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 @@ -886,17 +886,15 @@ public boolean isJoinedToTransaction() { } @Override - public T unwrap(Class cls) { - if ( cls.isAssignableFrom( Session.class ) ) { - //noinspection unchecked - return (T) this; - } - return this.lazySession.get().unwrap( cls ); + public T unwrap(Class type) { + return type.isAssignableFrom( Session.class ) + ? type.cast( this ) + : lazySession.get().unwrap( type ); } @Override public Object getDelegate() { - return this.lazySession.get().getDelegate(); + return lazySession.get().getDelegate(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultReplicateEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultReplicateEventListener.java index 710c4593eccc..c1db00d2a228 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultReplicateEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultReplicateEventListener.java @@ -114,7 +114,17 @@ else if ( EVENT_LISTENER_LOGGER.isTraceEnabled() ) { private static boolean shouldOverwrite( ReplicationMode replicationMode, Object entityVersion, Object realOldVersion, BasicType versionType) { - return replicationMode.shouldOverwriteCurrentVersion( (T) realOldVersion, (T) entityVersion, versionType ); + return replicationMode.shouldOverwriteCurrentVersion( + castVersion( realOldVersion, versionType ), + castVersion( entityVersion, versionType ), + versionType + ); + } + + private static T castVersion(Object realOldVersion, BasicType versionType) { + return versionType == null + ? null + : versionType.getJavaTypeDescriptor().cast( realOldVersion ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerRegistryImpl.java b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerRegistryImpl.java index 2bda60e2a49b..445ac91b47c8 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerRegistryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerRegistryImpl.java @@ -135,10 +135,9 @@ private T[] resolveListenerInstances(EventType type, Class.. } private T resolveListenerInstance(Class listenerClass) { - @SuppressWarnings("unchecked") - final T listenerInstance = (T) listenerClassToInstanceMap.get( listenerClass ); + final T listenerInstance = listenerClass.cast( listenerClassToInstanceMap.get( listenerClass ) ); if ( listenerInstance == null ) { - T newListenerInstance = instantiateListener( listenerClass ); + final T newListenerInstance = instantiateListener( listenerClass ); listenerClassToInstanceMap.put( listenerClass, newListenerInstance ); return newListenerInstance; } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/EntityGraphs.java b/hibernate-core/src/main/java/org/hibernate/graph/EntityGraphs.java index 9d38c57924e1..9106be1fd9d9 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/EntityGraphs.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/EntityGraphs.java @@ -64,13 +64,13 @@ public static EntityGraph createGraph(EntityType rootType) { * @since 7.0 */ public static EntityGraph> createGraphForDynamicEntity(EntityType rootType) { - final EntityDomainType domainType = (EntityDomainType) rootType; + final var domainType = (EntityDomainType) rootType; if ( domainType.getRepresentationMode() != RepresentationMode.MAP ) { throw new IllegalArgumentException( "Entity '" + domainType.getHibernateEntityName() + "' is not a dynamic entity" ); } @SuppressWarnings("unchecked") //Safe, because we just checked - final EntityDomainType> dynamicEntity = (EntityDomainType>) domainType; + final var dynamicEntity = (EntityDomainType>) domainType; return new RootGraphImpl<>( null, dynamicEntity ); } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/GraphImpl.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/GraphImpl.java index cd40c149e714..b28471f203f8 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/GraphImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/GraphImpl.java @@ -182,7 +182,7 @@ private static void mergeNode( AttributeNodeImplementor node, AttributeNodeImplementor existingNode) { if ( existingNode.getAttributeDescriptor() == node.getAttributeDescriptor() ) { @SuppressWarnings("unchecked") // safe, we just checked - final AttributeNodeImplementor castNode = (AttributeNodeImplementor) node; + final var castNode = (AttributeNodeImplementor) node; existingNode.merge( castNode ); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 82b2f2e250a7..9f01e1fe356d 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -2789,23 +2789,24 @@ public Collection getManagedEntities(String entityName) { public Collection getManagedEntities(Class entityType) { return persistenceContext.getEntityHoldersByKey().entrySet().stream() .filter( entry -> entry.getKey().getPersister().getMappedClass().equals( entityType ) ) - .map( entry -> (E) entry.getValue().getManagedObject() ) + .map( entry -> entityType.cast( entry.getValue().getManagedObject() ) ) .toList(); } @Override public Collection getManagedEntities(EntityType entityType) { - final String entityName = ( (EntityDomainType) entityType ).getHibernateEntityName(); + final var entityDomainType = (EntityDomainType) entityType; + final String entityName = entityDomainType.getHibernateEntityName(); return persistenceContext.getEntityHoldersByKey().entrySet().stream() .filter( entry -> entry.getKey().getEntityName().equals( entityName ) ) - .map( entry -> (E) entry.getValue().getManagedObject() ) + .map( entry -> entityType.getJavaType().cast( entry.getValue().getManagedObject() ) ) .toList(); } /** - * Used by JDK serialization... + * Used by JDK serialization * - * @param oos The output stream to which we are being written... + * @param oos The output stream to which we are being written * * @throws IOException Indicates a general IO stream exception */ @@ -2824,9 +2825,9 @@ private void writeObject(ObjectOutputStream oos) throws IOException { } /** - * Used by JDK serialization... + * Used by JDK serialization * - * @param ois The input stream from which we are being read... + * @param ois The input stream from which we are being read * * @throws IOException Indicates a general IO stream exception * @throws ClassNotFoundException Indicates a class resolution issue @@ -2846,7 +2847,7 @@ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFound // LoadQueryInfluencers#getEnabledFilters() tries to validate each enabled // filter, which will fail when called before FilterImpl#afterDeserialize( factory ); - // Instead lookup the filter by name and then call FilterImpl#afterDeserialize( factory ). + // Instead, look up the filter by name and then call FilterImpl#afterDeserialize( factory ). for ( String filterName : loadQueryInfluencers.getEnabledFilterNames() ) { ( (FilterImpl) loadQueryInfluencers.getEnabledFilter( filterName ) ) .afterDeserialize( getFactory() ); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/type/PrimitiveWrapperHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/type/PrimitiveWrapperHelper.java index e9c2de5052d0..d2e88c50ea20 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/type/PrimitiveWrapperHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/type/PrimitiveWrapperHelper.java @@ -21,6 +21,18 @@ public interface PrimitiveWrapperDescriptor { Class getWrapperClass(); } + public static X cast(Class type, Object value) { + return type.isPrimitive() + ? getDescriptorByPrimitiveType( type ).getWrapperClass().cast( value ) + : type.cast( value ); + } + + public static boolean isInstance(Class type, Object value) { + return type.isPrimitive() + ? getDescriptorByPrimitiveType( type ).getWrapperClass().isInstance( value ) + : type.isInstance( value ); + } + public static class BooleanDescriptor implements PrimitiveWrapperDescriptor { public static final BooleanDescriptor INSTANCE = new BooleanDescriptor(); diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java index 8d82e0320a57..b05f5da713ed 100644 --- a/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java @@ -1515,8 +1515,8 @@ private String exceptionHeader() { @SuppressWarnings("unchecked") private T loadSettingInstance(String settingName, Object settingValue, Class clazz) { final Class instanceClass; - if ( clazz.isAssignableFrom( settingValue.getClass() ) ) { - return (T) settingValue; + if ( clazz.isInstance( settingValue ) ) { + return clazz.cast( settingValue ); } else if ( settingValue instanceof Class ) { instanceClass = (Class) settingValue; diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/spi/JpaCompliance.java b/hibernate-core/src/main/java/org/hibernate/jpa/spi/JpaCompliance.java index 630800df6e7b..af90dae005bc 100644 --- a/hibernate-core/src/main/java/org/hibernate/jpa/spi/JpaCompliance.java +++ b/hibernate-core/src/main/java/org/hibernate/jpa/spi/JpaCompliance.java @@ -123,7 +123,7 @@ public interface JpaCompliance { * {@link jakarta.persistence.EntityManager#find} should be exactly the * expected type, allowing no type coercion. *

- * Historically, Hibernate behaved the same way. Since 6.0 however, + * Historically, Hibernate behaved the same way. Since 6.0, however, * Hibernate has the ability to coerce the passed type to the expected * type. For example, an {@link Integer} may be widened to {@link Long}. * Coercion is performed by calling diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java index 24e8f5f49fbb..a792b296e9cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java @@ -20,6 +20,7 @@ import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.exec.spi.JdbcSelectExecutor; +import org.hibernate.type.descriptor.java.JavaType; import java.util.ArrayList; import java.util.List; @@ -39,11 +40,17 @@ public abstract class AbstractMultiIdEntityLoader implements MultiIdEntityLoa private final EntityMappingType entityDescriptor; private final SessionFactoryImplementor sessionFactory; protected final EntityIdentifierMapping identifierMapping; + private final boolean idCoercionEnabled; public AbstractMultiIdEntityLoader(EntityMappingType entityDescriptor, SessionFactoryImplementor sessionFactory) { this.entityDescriptor = entityDescriptor; this.sessionFactory = sessionFactory; identifierMapping = getLoadable().getIdentifierMapping(); + idCoercionEnabled = + !sessionFactory.getSessionFactoryOptions() + .getJpaCompliance().isLoadByIdComplianceEnabled() + // special handling for entity with @IdClass + && !entityDescriptor.getIdentifierMapping().isVirtual(); } protected EntityMappingType getEntityDescriptor() { @@ -111,7 +118,6 @@ private List orderedMultiLoad( Object[] ids, MultiIdLoadOptions loadOptions, SharedSessionContractImplementor session) { - final boolean idCoercionEnabled = isIdCoercionEnabled(); final var idType = getLoadable().getIdentifierMapping().getJavaType(); final int maxBatchSize = maxBatchSize( ids, loadOptions ); @@ -124,7 +130,7 @@ private List orderedMultiLoad( final var lockOptions = lockOptions( loadOptions ); for ( int i = 0; i < ids.length; i++ ) { - final Object id = idCoercionEnabled ? idType.coerce( ids[i], session ) : ids[i]; + final Object id = coerce( session, idType, ids[i] ); final var entityKey = new EntityKey( id, getLoadable().getEntityPersister() ); if ( !loadFromEnabledCaches( loadOptions, session, id, lockOptions, entityKey, results, i ) ) { // if we did not hit any of the continues above, @@ -154,10 +160,13 @@ private List orderedMultiLoad( return (List) results; } + private Object coerce(SharedSessionContractImplementor session, JavaType idType, Object id) { + return idCoercionEnabled ? idType.coerce( id, session ) : id; + } + private static LockOptions lockOptions(MultiIdLoadOptions loadOptions) { - return loadOptions.getLockOptions() == null - ? new LockOptions( LockMode.NONE ) - : loadOptions.getLockOptions(); + final var lockOptions = loadOptions.getLockOptions(); + return lockOptions == null ? new LockOptions( LockMode.NONE ) : lockOptions; } protected abstract int maxBatchSize(Object[] ids, MultiIdLoadOptions loadOptions); @@ -169,8 +178,8 @@ private void handleResults( List results) { final var persistenceContext = session.getPersistenceContext(); for ( Integer position : elementPositionsLoadedByBatch ) { - // the element value at this position in the results List should be - // the EntityKey for that entity - reuse it + // the element value at this position in the results List + // should be the EntityKey for that entity - reuse it final var entityKey = (EntityKey) results.get( position ); session.getPersistenceContextInternal().getBatchFetchQueue().removeBatchLoadableEntityKey( entityKey ); final Object entity = persistenceContext.getEntity( entityKey ); @@ -330,11 +339,10 @@ private List unresolvedIds( LockOptions lockOptions, SharedSessionContractImplementor session, ResolutionConsumer resolutionConsumer) { - final boolean idCoercionEnabled = isIdCoercionEnabled(); final var idType = getLoadable().getIdentifierMapping().getJavaType(); List unresolvedIds = null; for ( int i = 0; i < ids.length; i++ ) { - final Object id = idCoercionEnabled ? idType.coerce( ids[i], session ) : ids[i]; + final Object id = coerce( session, idType, ids[i] ); unresolvedIds = loadFromCaches( loadOptions, @@ -353,10 +361,6 @@ private List unresolvedIds( // Depending on the implementation, a specific subtype of Object[] (e.g. Integer[]) may be needed. protected abstract Object[] toIdArray(List ids); - private boolean isIdCoercionEnabled() { - return !getSessionFactory().getSessionFactoryOptions().getJpaCompliance().isLoadByIdComplianceEnabled(); - } - public interface ResolutionConsumer { void consume(int position, EntityKey entityKey, T resolvedRef); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/IdentifierLoadAccessImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/IdentifierLoadAccessImpl.java index f8d38d956b43..d947ad688912 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/IdentifierLoadAccessImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/IdentifierLoadAccessImpl.java @@ -196,7 +196,10 @@ protected Object coerceId(Object id, SessionFactoryImplementor factory) { } else { try { - return entityPersister.getIdentifierMapping().getJavaType().coerce( id, this ); + final var identifierMapping = entityPersister.getIdentifierMapping(); + return identifierMapping.isVirtual() + ? id // special case for a class with an @IdClass + : identifierMapping.getJavaType().coerce( id, this ); } catch ( Exception e ) { throw new IllegalArgumentException( "Argument '" + id diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ValueMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ValueMapping.java index d2069e238121..e2f730dd19f3 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ValueMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ValueMapping.java @@ -40,8 +40,7 @@ default JavaType getExpressibleJavaType() { */ default X treatAs(Class targetType) { if ( targetType.isInstance( this ) ) { - //noinspection unchecked - return (X) this; + return targetType.cast( this ); } throw new IllegalArgumentException( diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NonAggregatedIdentifierMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NonAggregatedIdentifierMappingImpl.java index b81f892f4ff0..7dff3a5b2219 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NonAggregatedIdentifierMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NonAggregatedIdentifierMappingImpl.java @@ -65,12 +65,12 @@ public NonAggregatedIdentifierMappingImpl( super( entityPersister, rootTableName, creationProcess ); entityDescriptor = entityPersister; - if ( bootEntityDescriptor.getIdentifierMapper() == null - || bootEntityDescriptor.getIdentifierMapper() == bootEntityDescriptor.getIdentifier() ) { + final var identifierMapper = bootEntityDescriptor.getIdentifierMapper(); + final var identifier = bootEntityDescriptor.getIdentifier(); + if ( identifierMapper == null || identifierMapper == identifier ) { // cid -> getIdentifier // idClass -> null - final Component virtualIdSource = (Component) bootEntityDescriptor.getIdentifier(); - + final var virtualIdSource = (Component) identifier; virtualIdEmbeddable = new VirtualIdEmbeddable( virtualIdSource, this, @@ -85,9 +85,8 @@ public NonAggregatedIdentifierMappingImpl( else { // cid = getIdentifierMapper // idClass = getIdentifier - final var virtualIdSource = bootEntityDescriptor.getIdentifierMapper(); - final var idClassSource = (Component) bootEntityDescriptor.getIdentifier(); - + final var virtualIdSource = identifierMapper; + final var idClassSource = (Component) identifier; virtualIdEmbeddable = new VirtualIdEmbeddable( virtualIdSource, this, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java index e412aa39bb02..410ae933c825 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java @@ -137,8 +137,8 @@ public EntityDomainType findEntityType(@Nullable String entityName) { return null; } - final ManagedDomainType managedType = managedTypeByName.get( entityName ); - if ( managedType instanceof EntityDomainType entityDomainType ){ + if ( managedTypeByName.get( entityName ) + instanceof EntityDomainType entityDomainType ){ return entityDomainType; } @@ -148,7 +148,7 @@ public EntityDomainType findEntityType(@Nullable String entityName) { // was no direct match, we need to iterate over all of the and look based on // JPA entity-name. - for ( Map.Entry> entry : managedTypeByName.entrySet() ) { + for ( var entry : managedTypeByName.entrySet() ) { if ( entry.getValue() instanceof EntityDomainType possibility ) { if ( entityName.equals( possibility.getName() ) ) { return possibility; @@ -176,7 +176,7 @@ public EmbeddableDomainType findEmbeddableType(@Nullable String embeddableNam if ( embeddableName == null ) { return null; } - final ManagedDomainType managedType = managedTypeByName.get( embeddableName ); + final var managedType = managedTypeByName.get( embeddableName ); if ( !( managedType instanceof EmbeddableDomainType embeddableDomainType ) ) { return null; } @@ -185,7 +185,7 @@ public EmbeddableDomainType findEmbeddableType(@Nullable String embeddableNam @Override public EmbeddableDomainType embeddable(String embeddableName) { - final EmbeddableDomainType embeddableType = findEmbeddableType( embeddableName ); + final var embeddableType = findEmbeddableType( embeddableName ); if ( embeddableType == null ) { throw new IllegalArgumentException( "Not an embeddable: " + embeddableName ); } @@ -195,21 +195,21 @@ public EmbeddableDomainType embeddable(String embeddableName) { @Override public EntityDomainType getHqlEntityReference(String entityName) { Class loadedClass = null; - final ImportInfo importInfo = resolveImport( entityName ); + final var importInfo = (ImportInfo) resolveImport( entityName ); if ( importInfo != null ) { loadedClass = importInfo.loadedClass; entityName = importInfo.importedName; } - final EntityDomainType entityDescriptor = findEntityType( entityName ); + final var entityDescriptor = findEntityType( entityName ); if ( entityDescriptor != null ) { //noinspection unchecked return (EntityDomainType) entityDescriptor; } if ( loadedClass == null ) { - loadedClass = resolveRequestedClass( entityName ); - // populate class cache for boot metamodel imports + loadedClass = (Class) resolveRequestedClass( entityName ); + // populate the class cache for boot metamodel imports if ( importInfo != null && loadedClass != null ) { importInfo.loadedClass = loadedClass; } @@ -238,7 +238,7 @@ public ManagedDomainType findManagedType(Class cls) { @Override public ManagedDomainType managedType(Class cls) { - final ManagedDomainType type = findManagedType( cls ); + final var type = findManagedType( cls ); if ( type == null ) { // per JPA throw new IllegalArgumentException( "Not a managed type: " + cls ); @@ -249,17 +249,16 @@ public ManagedDomainType managedType(Class cls) { @Override @Nullable public EntityDomainType findEntityType(Class cls) { - final ManagedType type = managedTypeByClass.get( cls ); - if ( !( type instanceof EntityDomainType ) ) { - return null; - } + final var type = managedTypeByClass.get( cls ); //noinspection unchecked - return (EntityDomainType) type; + return type instanceof EntityDomainType + ? (EntityDomainType) type + : null; } @Override public EntityDomainType entity(Class cls) { - final EntityDomainType entityType = findEntityType( cls ); + final var entityType = findEntityType( cls ); if ( entityType == null ) { throw new IllegalArgumentException( "Not an entity: " + cls.getName() ); } @@ -268,17 +267,16 @@ public EntityDomainType entity(Class cls) { @Override public @Nullable EmbeddableDomainType findEmbeddableType(Class cls) { - final ManagedType type = managedTypeByClass.get( cls ); - if ( !( type instanceof EmbeddableDomainType ) ) { - return null; - } + final var type = managedTypeByClass.get( cls ); //noinspection unchecked - return (EmbeddableDomainType) type; + return type instanceof EmbeddableDomainType + ? (EmbeddableDomainType) type + : null; } @Override public EmbeddableDomainType embeddable(Class cls) { - final EmbeddableDomainType embeddableType = findEmbeddableType( cls ); + final var embeddableType = findEmbeddableType( cls ); if ( embeddableType == null ) { throw new IllegalArgumentException( "Not an embeddable: " + cls.getName() ); } @@ -322,7 +320,7 @@ public Set> getEmbeddables() { @Override public EnumJavaType getEnumType(String className) { - final EnumJavaType enumJavaType = enumJavaTypes.get( className ); + final var enumJavaType = enumJavaTypes.get( className ); if ( enumJavaType != null ) { return enumJavaType; } @@ -383,7 +381,7 @@ private Field getJavaField(String className, String fieldName) throws NoSuchFiel @Override public void addNamedEntityGraph(String graphName, RootGraphImplementor rootGraph) { - final EntityGraph old = entityGraphMap.put( graphName, rootGraph.makeImmutableCopy( graphName ) ); + final var old = entityGraphMap.put( graphName, rootGraph.makeImmutableCopy( graphName ) ); if ( old != null ) { CORE_LOGGER.tracef( "EntityGraph named '%s' was replaced", graphName ); } @@ -396,7 +394,7 @@ public RootGraphImplementor findEntityGraphByName(String name) { @Override public List> findEntityGraphsByJavaType(Class entityClass) { - final EntityDomainType entityType = entity( entityClass ); + final var entityType = entity( entityClass ); if ( entityType == null ) { throw new IllegalArgumentException( "Given class is not an entity: " + entityClass.getName() ); } @@ -415,7 +413,7 @@ public List> findEntityGraphsByJavaType(Class enti @Override public Map> getNamedEntityGraphs(Class entityClass) { - final EntityDomainType entityType = entity( entityClass ); + final var entityType = entity( entityClass ); if ( entityType == null ) { throw new IllegalArgumentException( "Given class is not an entity: " + entityClass.getName() ); } @@ -438,12 +436,11 @@ public String qualifyImportableName(String queryName) { return importInfo == null ? null : importInfo.importedName; } - private ImportInfo resolveImport(final String name) { - final ImportInfo importInfo = nameToImportMap.get( name ); + private ImportInfo resolveImport(final String name) { + final var importInfo = nameToImportMap.get( name ); //optimal path first if ( importInfo != null ) { - //noinspection unchecked - return (ImportInfo) importInfo; + return importInfo; } else { //then check the negative cache, to avoid bothering the classloader unnecessarily @@ -451,10 +448,10 @@ private ImportInfo resolveImport(final String name) { return null; } else { - // see if the name is a fully-qualified class name - final Class loadedClass = resolveRequestedClass( name ); + // see if the name is a fully qualified class name + final Class loadedClass = resolveRequestedClass( name ); if ( loadedClass == null ) { - // it is NOT a fully-qualified class name - add a marker entry so we do not keep trying later + // it is NOT a fully qualified class name - add a marker entry, so we do not keep trying later // note that ConcurrentHashMap does not support null value so a marker entry is needed // [HHH-14948] But only add it if the cache size isn't getting too large, as in some use cases // the queries are dynamically generated and this cache could lead to memory leaks when left unbounded. @@ -470,9 +467,9 @@ private ImportInfo resolveImport(final String name) { return null; } else { - // it is a fully-qualified class name - add it to the cache + // it is a fully qualified class name - add it to the cache // so to not needing to load from the classloader again - final ImportInfo info = new ImportInfo<>( name, loadedClass ); + final ImportInfo info = new ImportInfo<>( name, loadedClass ); nameToImportMap.put( name, info ); return info; } @@ -481,24 +478,22 @@ private ImportInfo resolveImport(final String name) { } private void applyNamedEntityGraphs(Collection namedEntityGraphs) { - for ( NamedEntityGraphDefinition definition : namedEntityGraphs ) { + for ( var definition : namedEntityGraphs ) { CORE_LOGGER.tracef( "Applying named entity graph [name=%s, source=%s]", definition.name(), definition.source() ); final RootGraphImplementor graph = definition.graphCreator().createEntityGraph( (entityClass) -> { - final ManagedDomainType managedDomainType = managedTypeByClass.get( entityClass ); - if ( managedDomainType instanceof EntityDomainType match ) { + if ( managedTypeByClass.get( entityClass ) instanceof EntityDomainType match ) { return match; } throw new IllegalArgumentException( "Cannot resolve entity class : " + entityClass.getName() ); }, (jpaEntityName) -> { - for ( Map.Entry> entry : managedTypeByName.entrySet() ) { - if ( entry.getValue() instanceof EntityDomainType possibility ) { - if ( jpaEntityName.equals( possibility.getName() ) ) { - return possibility; - } + for ( var entry : managedTypeByName.entrySet() ) { + if ( entry.getValue() instanceof EntityDomainType possibility + && jpaEntityName.equals( possibility.getName() ) ) { + return possibility; } } throw new IllegalArgumentException( "Cannot resolve entity name : " + jpaEntityName ); @@ -509,7 +504,7 @@ private void applyNamedEntityGraphs(Collection named } - private Class resolveRequestedClass(String entityName) { + private Class resolveRequestedClass(String entityName) { try { return classLoaderService.classForName( entityName ); } @@ -522,7 +517,7 @@ private Class resolveRequestedClass(String entityName) { public EntityDomainType resolveEntityReference(Class javaType) { // try the incoming Java type as a "strict" entity reference { - final ManagedDomainType managedType = managedTypeByClass.get( javaType ); + final var managedType = managedTypeByClass.get( javaType ); if ( managedType instanceof EntityDomainType ) { return (EntityDomainType) managedType; } @@ -538,37 +533,34 @@ public EntityDomainType resolveEntityReference(Class javaType) { // otherwise, try to handle it as a polymorphic reference { - final EntityDomainType polymorphicDomainType = - (EntityDomainType) polymorphicEntityReferenceMap.get( javaType ); + final var polymorphicDomainType = + (EntityDomainType) + polymorphicEntityReferenceMap.get( javaType ); if ( polymorphicDomainType != null ) { return polymorphicDomainType; } // create a set of descriptors that should be used to build the polymorphic EntityDomainType final Set> matchingDescriptors = new HashSet<>(); - for ( ManagedDomainType managedType : managedTypeByName.values() ) { - if ( managedType.getPersistenceType() != Type.PersistenceType.ENTITY ) { - continue; - } - // see if we should add `entityDomainType` as one of the matching-descriptors. - if ( javaType.isAssignableFrom( managedType.getJavaType() ) ) { + for ( var managedType : managedTypeByName.values() ) { + if ( managedType.getPersistenceType() == Type.PersistenceType.ENTITY + // see if we should add EntityDomainType as one of the matching descriptors. + && javaType.isAssignableFrom( managedType.getJavaType() ) ) { // the queried type is assignable from the type of the current entity-type // we should add it to the collecting set of matching descriptors. it should // be added aside from a few cases... // if the managed-type has a super type and the java type is assignable from the super type, - // do not add the managed-type as the super itself will get added and the initializers for + // do not add the managed type as the super itself will get added and the initializers for // entity mappings already handle loading subtypes - adding it would be redundant and lead to // incorrect results - final ManagedDomainType superType = managedType.getSuperType(); - if ( superType != null - && superType.getPersistenceType() == Type.PersistenceType.ENTITY - && javaType.isAssignableFrom( superType.getJavaType() ) ) { - continue; + final var superType = managedType.getSuperType(); + if ( superType == null + || superType.getPersistenceType() != Type.PersistenceType.ENTITY + || !javaType.isAssignableFrom( superType.getJavaType() ) ) { + matchingDescriptors.add( (EntityDomainType) managedType ); } - // otherwise, add it - matchingDescriptors.add( (EntityDomainType) managedType ); } } @@ -604,10 +596,11 @@ public void processJpa( Collection namedEntityGraphDefinitions, RuntimeModelCreationContext runtimeModelCreationContext) { bootMetamodel.getImports() - .forEach( (key, value) -> this.nameToImportMap.put( key, new ImportInfo<>( value, null ) ) ); + .forEach( (key, value) -> nameToImportMap.put( key, + new ImportInfo<>( value, null ) ) ); this.entityProxyInterfaceMap.putAll( entityProxyInterfaceMap ); - final MetadataContext context = new MetadataContext( + final var context = new MetadataContext( this, mappingMetamodel, bootMetamodel, @@ -628,31 +621,31 @@ public void processJpa( this.jpaMetaModelPopulationSetting = jpaMetaModelPopulationSetting; // Identifiable types (Entities and MappedSuperclasses) - this.managedTypeByName.putAll( context.getIdentifiableTypesByName() ); - this.managedTypeByClass.putAll( context.getEntityTypeMap() ); - this.managedTypeByClass.putAll( context.getMappedSuperclassTypeMap() ); + managedTypeByName.putAll( context.getIdentifiableTypesByName() ); + managedTypeByClass.putAll( context.getEntityTypeMap() ); + managedTypeByClass.putAll( context.getMappedSuperclassTypeMap() ); // Embeddable types int mapEmbeddables = 0; - for ( EmbeddableDomainType embeddable : context.getEmbeddableTypeSet() ) { + for ( var embeddable : context.getEmbeddableTypeSet() ) { // Do not register the embeddable types for id classes if ( embeddable.getExpressibleJavaType() instanceof EntityJavaType ) { continue; } - final Class embeddableClass = embeddable.getJavaType(); + final var embeddableClass = embeddable.getJavaType(); if ( embeddableClass != Map.class ) { - this.managedTypeByClass.put( embeddable.getJavaType(), embeddable ); - this.managedTypeByName.put( embeddable.getTypeName(), embeddable ); + managedTypeByClass.put( embeddable.getJavaType(), embeddable ); + managedTypeByName.put( embeddable.getTypeName(), embeddable ); } else { - this.managedTypeByName.put( "dynamic-embeddable-" + mapEmbeddables++, embeddable ); + managedTypeByName.put( "dynamic-embeddable-" + mapEmbeddables++, embeddable ); } } typeConfiguration.getJavaTypeRegistry().forEachDescriptor( descriptor -> { if ( descriptor instanceof EnumJavaType> enumJavaType ) { - final Class> enumJavaClass = enumJavaType.getJavaTypeClass(); - for ( Enum enumConstant : enumJavaClass.getEnumConstants() ) { + final var enumJavaClass = enumJavaType.getJavaTypeClass(); + for ( var enumConstant : enumJavaClass.getEnumConstants() ) { addAllowedEnumLiteralsToEnumTypesMap( allowedEnumLiteralsToEnumTypeNames, enumConstant.name(), @@ -731,7 +724,8 @@ private EntityTypeImpl buildEntityType( MetadataContext context, TypeConfiguration typeConfiguration) { context.pushEntityWorkedOn( persistentClass ); - final var entityType = entityType( persistentClass, persistentClass.getMappedClass(), context, typeConfiguration ); + final var entityType = + entityType( persistentClass, persistentClass.getMappedClass(), context, typeConfiguration ); context.registerEntityType( persistentClass, entityType ); context.popEntityWorkedOn( persistentClass ); return entityType; @@ -760,9 +754,9 @@ private EntityTypeImpl entityType( } private void handleUnusedMappedSuperclasses(MetadataContext context, TypeConfiguration typeConfiguration) { - final Set unusedMappedSuperclasses = context.getUnusedMappedSuperclasses(); + final var unusedMappedSuperclasses = context.getUnusedMappedSuperclasses(); if ( !unusedMappedSuperclasses.isEmpty() ) { - for ( MappedSuperclass mappedSuperclass : unusedMappedSuperclasses ) { + for ( var mappedSuperclass : unusedMappedSuperclasses ) { CORE_LOGGER.unusedMappedSuperclass( mappedSuperclass.getMappedClass().getName() ); locateOrBuildMappedSuperclassType( mappedSuperclass, context, typeConfiguration ); } @@ -773,7 +767,7 @@ private MappedSuperclassDomainType locateOrBuildMappedSuperclassType( MappedSuperclass mappedSuperclass, MetadataContext context, TypeConfiguration typeConfiguration) { - final MappedSuperclassDomainType mappedSuperclassType = + final var mappedSuperclassType = context.locateMappedSuperclassType( mappedSuperclass ); return mappedSuperclassType == null ? buildMappedSuperclassType( mappedSuperclass, mappedSuperclass.getMappedClass(), context, typeConfiguration ) @@ -786,13 +780,13 @@ private MappedSuperclassTypeImpl buildMappedSuperclassType( MetadataContext context, TypeConfiguration typeConfiguration) { @SuppressWarnings("unchecked") - final IdentifiableDomainType superType = + final var superType = (IdentifiableDomainType) supertypeForMappedSuperclass( mappedSuperclass, context, typeConfiguration ); - final JavaType javaType = + final var javaType = context.getTypeConfiguration().getJavaTypeRegistry() .resolveManagedTypeDescriptor( mappedClass ); - final MappedSuperclassTypeImpl mappedSuperclassType = + final var mappedSuperclassType = new MappedSuperclassTypeImpl<>( javaType, mappedSuperclass, superType, this ); context.registerMappedSuperclassType( mappedSuperclass, mappedSuperclassType ); return mappedSuperclassType; @@ -802,15 +796,14 @@ private IdentifiableDomainType supertypeForPersistentClass( PersistentClass persistentClass, MetadataContext context, TypeConfiguration typeConfiguration) { - final MappedSuperclass superMappedSuperclass = persistentClass.getSuperMappedSuperclass(); - final IdentifiableDomainType supertype = + final var superMappedSuperclass = persistentClass.getSuperMappedSuperclass(); + final var supertype = superMappedSuperclass == null ? null : locateOrBuildMappedSuperclassType( superMappedSuperclass, context, typeConfiguration ); - - //no mappedSuperclass, check for a super entity if ( supertype == null ) { - final PersistentClass superPersistentClass = persistentClass.getSuperclass(); + // no mapped superclass, check for a super entity + final var superPersistentClass = persistentClass.getSuperclass(); return superPersistentClass == null ? null : locateOrBuildEntityType( superPersistentClass, context, typeConfiguration ); @@ -824,14 +817,14 @@ private IdentifiableDomainType supertypeForMappedSuperclass( MappedSuperclass mappedSuperclass, MetadataContext context, TypeConfiguration typeConfiguration) { - final MappedSuperclass superMappedSuperclass = mappedSuperclass.getSuperMappedSuperclass(); - final IdentifiableDomainType superType = + final var superMappedSuperclass = mappedSuperclass.getSuperMappedSuperclass(); + final var superType = superMappedSuperclass == null ? null : locateOrBuildMappedSuperclassType( superMappedSuperclass, context, typeConfiguration ); - //no mappedSuperclass, check for a super entity if ( superType == null ) { - final PersistentClass superPersistentClass = mappedSuperclass.getSuperPersistentClass(); + //no mapped superclass, check for a super entity + final var superPersistentClass = mappedSuperclass.getSuperPersistentClass(); return superPersistentClass == null ? null : locateOrBuildEntityType( superPersistentClass, context, typeConfiguration ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/specification/internal/ProjectionSpecificationImpl.java b/hibernate-core/src/main/java/org/hibernate/query/specification/internal/ProjectionSpecificationImpl.java index 61954c799819..07fb2d33babc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/specification/internal/ProjectionSpecificationImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/specification/internal/ProjectionSpecificationImpl.java @@ -31,6 +31,8 @@ import java.util.Map; import java.util.function.BiFunction; +import static org.hibernate.internal.util.type.PrimitiveWrapperHelper.cast; + /** * @author Gavin King */ @@ -47,14 +49,14 @@ public ProjectionSpecificationImpl(SelectionSpecification selectionSpecificat public Element select(SingularAttribute attribute) { final int position = specifications.size(); specifications.add( (select, root) -> root.get( attribute ) ); - return tuple -> (X) tuple[position]; + return tuple -> cast( attribute.getJavaType(), tuple[position] ); } @Override public Element select(Path path) { final int position = specifications.size(); specifications.add( (select, root) -> (SqmPath) path.path( root ) ); - return tuple -> (X) tuple[position]; + return tuple -> cast( path.getType(), tuple[position] ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java index a82b490e1e55..b6678db8e43c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java @@ -758,6 +758,7 @@ public T getParameterValue(Parameter param) { } if ( binding.isMultiValued() ) { + // TODO: THIS IS UNSOUND //noinspection unchecked return (T) binding.getBindValues(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java index c9678488ca50..f96a66ad04ae 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java @@ -43,7 +43,6 @@ import org.hibernate.jpa.spi.JpaCompliance; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityVersionMapping; -import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.JpaMetamodel; import org.hibernate.metamodel.model.domain.PersistentAttribute; import org.hibernate.metamodel.model.domain.internal.EntitySqmPathSource; @@ -143,7 +142,6 @@ import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.descriptor.java.EnumJavaType; import org.hibernate.type.descriptor.java.JavaType; -import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.spi.TypeConfiguration; import jakarta.persistence.Tuple; @@ -1558,7 +1556,7 @@ private SqmLiteral createLiteral(T value, SqmBindableType expressible) final T coercedValue = expressible.getExpressibleJavaType() .coerce( value, this::getTypeConfiguration ); - // ignore typeInferenceSource and fallback the value type + // ignore typeInferenceSource and fall back to the value type return expressible.getExpressibleJavaType().isInstance( coercedValue ) ? new SqmLiteral<>( coercedValue, expressible, this ) : literal( value ); @@ -1579,9 +1577,9 @@ else if ( value == null ) { } private BasicType resolveInferredType(T value) { - final TypeConfiguration typeConfiguration = getTypeConfiguration(); - final Class type = ReflectHelper.getClass( value ); - final BasicType result = typeConfiguration.getBasicTypeForJavaType( type ); + final var typeConfiguration = getTypeConfiguration(); + final var type = ReflectHelper.getClass( value ); + final var result = typeConfiguration.getBasicTypeForJavaType( type ); if ( result == null && value instanceof Enum enumValue ) { return (BasicType) resolveEnumType( typeConfiguration, enumValue ); } @@ -1591,9 +1589,9 @@ private BasicType resolveInferredType(T value) { } private static > BasicType resolveEnumType(TypeConfiguration configuration, Enum enumValue) { - final EnumJavaType javaType = new EnumJavaType<>( ReflectHelper.getClass( enumValue ) ); - final JdbcType jdbcType = javaType.getRecommendedJdbcType( configuration.getCurrentBaseSqlTypeIndicators() ); - return configuration.getBasicTypeRegistry().resolve( javaType, jdbcType ); + final var enumJavaType = new EnumJavaType<>( ReflectHelper.getClass( enumValue ) ); + final var jdbcType = enumJavaType.getRecommendedJdbcType( configuration.getCurrentBaseSqlTypeIndicators() ); + return configuration.getBasicTypeRegistry().resolve( enumJavaType, jdbcType ); } @Override @@ -1647,9 +1645,10 @@ public SqmExpression nullLiteral(Class resultClass) { return new SqmLiteralNull<>( this ); } else { - final BasicType basicTypeForJavaType = getTypeConfiguration().getBasicTypeForJavaType( resultClass ); + final var basicTypeForJavaType = + getTypeConfiguration().getBasicTypeForJavaType( resultClass ); // if there's no basic type, it might be an entity type - final SqmBindableType sqmExpressible = + final var sqmExpressible = basicTypeForJavaType == null ? resolveExpressible( getDomainModel().managedType( resultClass ) ) : basicTypeForJavaType; @@ -1692,8 +1691,8 @@ public JpaCriteriaParameter parameter(Class paramClass) { @Override public JpaCriteriaParameter parameter(Class paramClass, @Nullable String name) { - final BasicType basicType = getTypeConfiguration().getBasicTypeForJavaType( paramClass ); - boolean notBasic = basicType == null; + final var basicType = getTypeConfiguration().getBasicTypeForJavaType( paramClass ); + final boolean notBasic = basicType == null; final BindableType parameterType = notBasic && Collection.class.isAssignableFrom( paramClass ) // a Collection-valued, multi-valued parameter @@ -1709,7 +1708,7 @@ public JpaParameterExpression> listParameter(Class paramClass) { @Override public JpaParameterExpression> listParameter(Class paramClass, @Nullable String name) { - final BindableType> parameterType = new MultiValueParameterType<>( (Class>) (Class) List.class ); + final var parameterType = new MultiValueParameterType<>( (Class>) (Class) List.class ); return new JpaCriteriaParameter<>( name, parameterType, true, this ); } @@ -2198,7 +2197,7 @@ private ValueBindJpaCriteriaParameter valueParameter(@Nullable T value, @ final T coercedValue = resolveExpressible( bindableType ).getExpressibleJavaType() .coerce( value, this::getTypeConfiguration ); - // ignore typeInferenceSource and fall back the value type + // ignore typeInferenceSource and fall back to the value type if ( isInstance( bindableType, coercedValue ) ) { @SuppressWarnings("unchecked") // safe, we just checked final var widerType = (BindableType) bindableType; @@ -2208,23 +2207,28 @@ private ValueBindJpaCriteriaParameter valueParameter(@Nullable T value, @ } private ValueBindJpaCriteriaParameter> collectionValueParameter(Collection value, SqmExpression elementTypeInferenceSource) { - BindableType bindableType = null; + final var elementType = + resolveExpressible( bindableType( elementTypeInferenceSource ) ) + .getSqmType(); + if ( elementType == null ) { + throw new UnsupportedOperationException( "Can't infer collection type based on element expression: " + elementTypeInferenceSource ); + } + final var collectionType = DdlTypeHelper.resolveListType( elementType, getTypeConfiguration() ); + //noinspection unchecked + return new ValueBindJpaCriteriaParameter<>( (BasicType>) collectionType, value, this ); + } + + private static BindableType bindableType(SqmExpression elementTypeInferenceSource) { if ( elementTypeInferenceSource != null ) { if ( elementTypeInferenceSource instanceof BindableType ) { //noinspection unchecked - bindableType = (BindableType) elementTypeInferenceSource; + return (BindableType) elementTypeInferenceSource; } else if ( elementTypeInferenceSource.getNodeType() != null ) { - bindableType = elementTypeInferenceSource.getNodeType(); + return elementTypeInferenceSource.getNodeType(); } } - final DomainType elementType = resolveExpressible( bindableType ).getSqmType(); - if ( elementType == null ) { - throw new UnsupportedOperationException( "Can't infer collection type based on element expression: " + elementTypeInferenceSource ); - } - final BasicType collectionType = DdlTypeHelper.resolveListType( elementType, getTypeConfiguration() ); - //noinspection unchecked - return new ValueBindJpaCriteriaParameter<>( (BasicType>) collectionType, value, this ); + return null; } private ValueBindJpaCriteriaParameter valueParameter(T value) { @@ -2287,16 +2291,11 @@ public SqmExpression nullif(Expression x, Y y) { } private SqmExpression createNullifFunctionNode(SqmExpression first, SqmExpression second) { - final SqmBindableType bindableType = - highestPrecedenceType( first.getExpressible(), second.getExpressible() ); + final var bindableType = highestPrecedenceType( first.getExpressible(), second.getExpressible() ); @SuppressWarnings("unchecked") - final ReturnableType resultType = - bindableType == null ? null : (ReturnableType) bindableType.getSqmType(); - return getFunctionDescriptor( "nullif" ).generateSqmExpression( - asList( first, second ), - resultType, - getQueryEngine() - ); + final var resultType = bindableType == null ? null : (ReturnableType) bindableType.getSqmType(); + return getFunctionDescriptor( "nullif" ) + .generateSqmExpression( asList( first, second ), resultType, getQueryEngine() ); } private SqmFunctionDescriptor getFunctionDescriptor(String name) { @@ -2352,7 +2351,7 @@ public SqmPredicate and(Predicate... restrictions) { } final List predicates = new ArrayList<>( restrictions.length ); - for ( Predicate expression : restrictions ) { + for ( var expression : restrictions ) { predicates.add( (SqmPredicate) expression ); } return new SqmJunctionPredicate( Predicate.BooleanOperator.AND, predicates, this ); @@ -2365,7 +2364,7 @@ public SqmPredicate and(List restrictions) { } final List predicates = new ArrayList<>( restrictions.size() ); - for ( Predicate expression : restrictions ) { + for ( var expression : restrictions ) { predicates.add( (SqmPredicate) expression ); } return new SqmJunctionPredicate( Predicate.BooleanOperator.AND, predicates, this ); @@ -2388,7 +2387,7 @@ public SqmPredicate or(Predicate... restrictions) { } final List predicates = new ArrayList<>( restrictions.length ); - for ( Predicate expression : restrictions ) { + for ( var expression : restrictions ) { predicates.add( (SqmPredicate) expression ); } return new SqmJunctionPredicate( Predicate.BooleanOperator.OR, predicates, this ); @@ -2401,7 +2400,7 @@ public SqmPredicate or(List restrictions) { } final List predicates = new ArrayList<>( restrictions.size() ); - for ( Predicate expression : restrictions ) { + for ( var expression : restrictions ) { predicates.add( (SqmPredicate) expression ); } return new SqmJunctionPredicate( Predicate.BooleanOperator.OR, predicates, this ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 07e04313402b..78483d6625b4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -5812,10 +5812,8 @@ else if ( Character.class.isAssignableFrom( valueConverter.getRelationalJavaType // so we allow coercion between the number types else if ( Number.class.isAssignableFrom( valueConverter.getRelationalJavaType().getJavaTypeClass() ) && value instanceof Number ) { - return valueConverter.getRelationalJavaType().coerce( - value, - creationContext::getTypeConfiguration - ); + return valueConverter.getRelationalJavaType() + .coerce( value, creationContext::getTypeConfiguration ); } else { throw new SemanticException( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java index dac9754aa13d..937e489afd3a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java @@ -33,7 +33,6 @@ import org.hibernate.query.hql.spi.SqmCreationState; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SqmPathSource; -import org.hibernate.query.sqm.spi.SqmCreationHelper; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.SqmRenderContext; @@ -66,6 +65,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; import static org.hibernate.query.sqm.internal.SqmUtil.findCompatibleFetchJoin; +import static org.hibernate.query.sqm.spi.SqmCreationHelper.buildRootNavigablePath; /** * Convenience base class for SqmFrom implementations @@ -100,7 +100,7 @@ protected AbstractSqmFrom( @Nullable String alias, NodeBuilder nodeBuilder) { super( - SqmCreationHelper.buildRootNavigablePath( entityType.getHibernateEntityName(), alias ), + buildRootNavigablePath( entityType.getHibernateEntityName(), alias ), (SqmEntityDomainType) entityType, null, nodeBuilder @@ -118,7 +118,6 @@ protected AbstractSqmFrom( @Nullable String alias, NodeBuilder nodeBuilder) { super( navigablePath, entityType, null, nodeBuilder ); - this.alias = alias; } @@ -136,16 +135,18 @@ protected void copyTo(AbstractSqmFrom target, SqmCopyContext context) { super.copyTo( target, context ); final var joins = this.joins; if ( joins != null ) { - final ArrayList> newJoins = new ArrayList<>( joins.size() ); - for ( SqmJoin join : joins ) { + final ArrayList> newJoins = + new ArrayList<>( joins.size() ); + for ( var join : joins ) { newJoins.add( join.copy( context ) ); } target.joins = newJoins; } final var treats = this.treats; if ( treats != null ) { - final ArrayList> newTreats = new ArrayList<>( treats.size() ); - for ( SqmTreatedFrom treat : treats ) { + final ArrayList> newTreats = + new ArrayList<>( treats.size() ); + for ( var treat : treats ) { newTreats.add( treat.copy( context ) ); } target.treats = newTreats; @@ -169,7 +170,7 @@ public SqmPath resolvePathPart( SqmCreationState creationState) { // Try to resolve an existing attribute join without ON clause SqmPath resolvedPath = null; - for ( SqmJoin sqmJoin : getSqmJoins() ) { + for ( var sqmJoin : getSqmJoins() ) { // We can only match singular joins here, as plural path parts are interpreted like sub-queries if ( sqmJoin instanceof SqmSingularJoin attributeJoin && name.equals( sqmJoin.getReferencedPathSource().getPathName() ) ) { @@ -268,7 +269,7 @@ public void visitSqmJoins(Consumer> consumer) { protected > @Nullable X findTreat(ManagedDomainType targetType, @Nullable String alias) { if ( treats != null ) { - for ( SqmTreatedFrom treat : treats ) { + for ( var treat : treats ) { if ( treat.getModel() == targetType ) { if ( Objects.equals( treat.getExplicitAlias(), alias ) ) { //noinspection unchecked @@ -332,7 +333,7 @@ public SqmSingularJoin join(SingularAttribute attribute) @Override public SqmSingularJoin join(SingularAttribute attribute, JoinType jt) { - final SqmSingularJoin join = + final var join = buildSingularJoin( (SqmSingularPersistentAttribute) attribute, SqmJoinType.from( jt ), false ); addSqmJoin( join ); @@ -352,8 +353,8 @@ public SqmEntityJoin join(EntityDomainType targetEntityDescriptor) @Override public SqmEntityJoin join(EntityDomainType targetEntityDescriptor, SqmJoinType joinType) { //noinspection unchecked - final SqmRoot root = (SqmRoot) findRoot(); - final SqmEntityJoin sqmEntityJoin = new SqmEntityJoin<>( + final var root = (SqmRoot) findRoot(); + final var sqmEntityJoin = new SqmEntityJoin<>( targetEntityDescriptor, generateAlias(), joinType, @@ -370,7 +371,7 @@ public SqmBagJoin join(CollectionAttribute attribute) { @Override public SqmBagJoin join(CollectionAttribute attribute, JoinType jt) { - final SqmBagJoin join = buildBagJoin( + final var join = buildBagJoin( (BagPersistentAttribute) attribute, SqmJoinType.from( jt ), false @@ -386,7 +387,7 @@ public SqmSetJoin join(SetAttribute attribute) { @Override public SqmSetJoin join(SetAttribute attribute, JoinType jt) { - final SqmSetJoin join = buildSetJoin( + final var join = buildSetJoin( (SetPersistentAttribute) attribute, SqmJoinType.from( jt ), false @@ -402,7 +403,7 @@ public SqmListJoin join(ListAttribute attribute) { @Override public SqmListJoin join(ListAttribute attribute, JoinType jt) { - final SqmListJoin join = buildListJoin( + final var join = buildListJoin( (ListPersistentAttribute) attribute, SqmJoinType.from( jt ), false @@ -418,7 +419,7 @@ public SqmMapJoin join(MapAttribute attribute) @Override public SqmMapJoin join(MapAttribute attribute, JoinType jt) { - final SqmMapJoin join = buildMapJoin( + final var join = buildMapJoin( (MapPersistentAttribute) attribute, SqmJoinType.from( jt ), false @@ -435,7 +436,7 @@ public SqmAttributeJoin join(String attributeName) { @Override @SuppressWarnings("unchecked") public SqmAttributeJoin join(String attributeName, JoinType jt) { - final SqmPathSource subPathSource = (SqmPathSource) + final var subPathSource = (SqmPathSource) getReferencedPathSource().getSubPathSource( attributeName ); return (SqmAttributeJoin) buildJoin( subPathSource, SqmJoinType.from( jt ), false ); } @@ -448,10 +449,9 @@ public SqmBagJoin joinCollection(String attributeName) { @Override @SuppressWarnings("unchecked") public SqmBagJoin joinCollection(String attributeName, JoinType jt) { - final SqmPathSource joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName ); - + final var joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName ); if ( joinedPathSource instanceof BagPersistentAttribute ) { - final SqmBagJoin join = buildBagJoin( + final var join = buildBagJoin( (BagPersistentAttribute) joinedPathSource, SqmJoinType.from( jt ), false @@ -479,10 +479,9 @@ public SqmSetJoin joinSet(String attributeName) { @Override @SuppressWarnings("unchecked") public SqmSetJoin joinSet(String attributeName, JoinType jt) { - final SqmPathSource joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName ); - + final var joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName ); if ( joinedPathSource instanceof SetPersistentAttribute ) { - final SqmSetJoin join = buildSetJoin( + final var join = buildSetJoin( (SetPersistentAttribute) joinedPathSource, SqmJoinType.from( jt ), false @@ -510,10 +509,10 @@ public SqmListJoin joinList(String attributeName) { @Override @SuppressWarnings("unchecked") public SqmListJoin joinList(String attributeName, JoinType jt) { - final SqmPathSource joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName ); + final var joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName ); if ( joinedPathSource instanceof ListPersistentAttribute ) { - final SqmListJoin join = buildListJoin( + final var join = buildListJoin( (ListPersistentAttribute) joinedPathSource, SqmJoinType.from( jt ), false @@ -541,10 +540,10 @@ public SqmMapJoin joinMap(String attributeName) { @Override @SuppressWarnings("unchecked") public SqmMapJoin joinMap(String attributeName, JoinType jt) { - final SqmPathSource joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName ); + final var joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName ); if ( joinedPathSource instanceof MapPersistentAttribute ) { - final SqmMapJoin join = buildMapJoin( + final var join = buildMapJoin( (MapPersistentAttribute) joinedPathSource, SqmJoinType.from( jt ), false @@ -582,8 +581,8 @@ public SqmEntityJoin join(Class entityJavaType, JoinType joinType) @Override public SqmEntityJoin join(EntityType entity, JoinType joinType) { //noinspection unchecked - final SqmEntityJoin join = - new SqmEntityJoin<>( entity, generateAlias(), joinType, (SqmRoot) findRoot() ); + final var root = (SqmRoot) findRoot(); + final var join = new SqmEntityJoin<>( entity, generateAlias(), joinType, root ); addSqmJoin( join ); return join; } @@ -616,11 +615,11 @@ public JpaDerivedJoin join(Subquery subquery, SqmJoinType joinType, bo public JpaDerivedJoin join(Subquery subquery, SqmJoinType joinType, boolean lateral, String alias) { validateComplianceFromSubQuery(); //noinspection unchecked - final JpaDerivedJoin join = + final var derivedJoin = new SqmDerivedJoin<>( (SqmSubQuery) subquery, alias, joinType, lateral, (SqmRoot) findRoot() ); //noinspection unchecked - addSqmJoin( (SqmJoin) join ); - return join; + addSqmJoin( (SqmJoin) derivedJoin ); + return derivedJoin; } @Override @@ -636,11 +635,11 @@ public SqmJoin join(JpaCteCriteria cte, SqmJoinType joinType) { public SqmJoin join(JpaCteCriteria cte, SqmJoinType joinType, String alias) { validateComplianceFromSubQuery(); //noinspection unchecked - final SqmJoin join = + final var cteJoin = new SqmCteJoin<>( ( SqmCteStatement ) cte, alias, joinType, (SqmRoot) findRoot() ); //noinspection unchecked - addSqmJoin( (SqmJoin) join ); - return join; + addSqmJoin( (SqmJoin) cteJoin ); + return cteJoin; } @Override @@ -667,7 +666,7 @@ public JpaFunctionJoin join(JpaSetReturningFunction function) { public JpaFunctionJoin join(JpaSetReturningFunction function, SqmJoinType joinType, boolean lateral) { validateComplianceFromFunction(); //noinspection unchecked - final SqmFunctionJoin join = + final var functionJoin = new SqmFunctionJoin<>( (SqmSetReturningFunction) function, generateAlias(), @@ -675,8 +674,8 @@ public JpaFunctionJoin join(JpaSetReturningFunction function, SqmJoinT (SqmRoot) findRoot() ); //noinspection unchecked - addSqmJoin( (SqmJoin) join ); - return join; + addSqmJoin( (SqmJoin) functionJoin ); + return functionJoin; } @Override @@ -744,7 +743,7 @@ public JpaCrossJoin crossJoin(Class entityJavaType) { @Override public JpaCrossJoin crossJoin(EntityDomainType entity) { - final SqmCrossJoin crossJoin = + final var crossJoin = new SqmCrossJoin<>( (SqmEntityDomainType) entity, generateAlias(), findRoot() ); // noinspection unchecked addSqmJoin( (SqmJoin) crossJoin ); @@ -768,14 +767,13 @@ public SqmSingularJoin fetch(SingularAttribute attribute) @Override public SqmSingularJoin fetch(SingularAttribute attribute, JoinType jt) { final var persistentAttribute = (SqmSingularPersistentAttribute) attribute; - final SqmAttributeJoin compatibleFetchJoin = + final var compatibleFetchJoin = findCompatibleFetchJoin( this, persistentAttribute, SqmJoinType.from( jt ) ); if ( compatibleFetchJoin != null ) { return (SqmSingularJoin) compatibleFetchJoin; } - final SqmSingularJoin join = - buildSingularJoin( persistentAttribute, SqmJoinType.from( jt ), true ); + final var join = buildSingularJoin( persistentAttribute, SqmJoinType.from( jt ), true ); addSqmJoin( join ); return join; } @@ -816,15 +814,14 @@ private SqmAttributeJoin buildJoin( SqmJoinType joinType, boolean fetched) { if ( fetched ) { - final SqmAttributeJoin compatibleFetchJoin = + final var compatibleFetchJoin = findCompatibleFetchJoin( this, joinedPathSource, joinType ); if ( compatibleFetchJoin != null ) { return compatibleFetchJoin; } } - final SqmAttributeJoin sqmJoin = - buildAttributeJoin( joinedPathSource, joinType, fetched ); + final var sqmJoin = buildAttributeJoin( joinedPathSource, joinType, fetched ); addSqmJoin( sqmJoin ); return sqmJoin; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonNullBehavior.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonNullBehavior.java index 7cab17b40b01..8bc7759db8a3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonNullBehavior.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonNullBehavior.java @@ -52,12 +52,7 @@ public X accept(SemanticQueryWalker walker) { @Override public void appendHqlString(StringBuilder hql, SqmRenderContext context) { - if ( this == NULL ) { - hql.append( " null on null" ); - } - else { - hql.append( " absent on null" ); - } + hql.append( this == NULL ? " null on null" : " absent on null" ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/AbstractSqmSelectQuery.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/AbstractSqmSelectQuery.java index 996ba715ca3e..eb8006d81821 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/AbstractSqmSelectQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/AbstractSqmSelectQuery.java @@ -90,7 +90,7 @@ public AbstractSqmSelectQuery( protected Map> copyCteStatements(SqmCopyContext context) { final Map> copies = new LinkedHashMap<>( cteStatements.size() ); - for ( Map.Entry> entry : cteStatements.entrySet() ) { + for ( var entry : cteStatements.entrySet() ) { copies.put( entry.getKey(), entry.getValue().copy( context ) ); } return copies; @@ -181,7 +181,7 @@ private String validateCteName(String name) { } protected JpaCteCriteria withInternal(String name, AbstractQuery criteria) { - final SqmCteStatement cteStatement = new SqmCteStatement<>( + final var cteStatement = new SqmCteStatement<>( name, (SqmSelectQuery) criteria, this, @@ -198,7 +198,7 @@ protected JpaCteCriteria withInternal( AbstractQuery baseCriteria, boolean unionDistinct, Function, AbstractQuery> recursiveCriteriaProducer) { - final SqmCteStatement cteStatement = new SqmCteStatement<>( + final var cteStatement = new SqmCteStatement<>( name, (SqmSelectQuery) baseCriteria, unionDistinct, @@ -247,7 +247,7 @@ public List> getRootList() { * @see org.hibernate.query.criteria.JpaCriteriaQuery#getRoot(int, Class) */ public JpaRoot getRoot(int position, Class type) { - final List> rootList = getQuerySpec().getRootList(); + final var rootList = getQuerySpec().getRootList(); if ( rootList.size() <= position ) { throw new IllegalArgumentException( "Not enough root entities" ); } @@ -258,7 +258,7 @@ public JpaRoot getRoot(int position, Class type) { * @see org.hibernate.query.criteria.JpaCriteriaQuery#getRoot(String, Class) */ public JpaRoot getRoot(String alias, Class type) { - for ( SqmRoot root : getQuerySpec().getRootList() ) { + for ( var root : getQuerySpec().getRootList() ) { final String rootAlias = root.getAlias(); if ( rootAlias != null && rootAlias.equals( alias ) ) { return castRoot( root, type ); @@ -268,7 +268,7 @@ public JpaRoot getRoot(String alias, Class type) { } private static JpaRoot castRoot(JpaRoot root, Class type) { - final Class rootEntityType = root.getJavaType(); + final var rootEntityType = root.getJavaType(); if ( rootEntityType == null ) { throw new AssertionFailure( "Java type of root entity was null" ); } @@ -277,7 +277,7 @@ private static JpaRoot castRoot(JpaRoot root, Class type) + "' did not have the given type '" + type.getTypeName() + "'"); } @SuppressWarnings("unchecked") // safe, we just checked - final JpaRoot result = (JpaRoot) root; + final var result = (JpaRoot) root; return result; } @@ -296,20 +296,20 @@ public SqmRoot from(Class entityClass) { @Override public SqmDerivedRoot from(Subquery subquery) { validateComplianceFromSubQuery(); - final SqmDerivedRoot root = new SqmDerivedRoot<>( (SqmSubQuery) subquery, null ); + final var root = new SqmDerivedRoot<>( (SqmSubQuery) subquery, null ); addRoot( root ); return root; } public JpaRoot from(JpaCteCriteria cte) { - final SqmCteRoot root = new SqmCteRoot<>( ( SqmCteStatement ) cte, null ); + final var root = new SqmCteRoot<>( ( SqmCteStatement ) cte, null ); addRoot( root ); return root; } @Override public JpaFunctionRoot from(JpaSetReturningFunction function) { - final SqmFunctionRoot root = new SqmFunctionRoot<>( (SqmSetReturningFunction) function, null ); + final var root = new SqmFunctionRoot<>( (SqmSetReturningFunction) function, null ); addRoot( root ); return root; } @@ -355,8 +355,8 @@ public SqmSelectQuery distinct(boolean distinct) { @Override public @Nullable JpaSelection getSelection() { - final SqmSelectClause selectClause = getQuerySpec().getSelectClause(); - final List> selections = selectClause.getSelections(); + final var selectClause = getQuerySpec().getSelectClause(); + final var selections = selectClause.getSelections(); return (JpaSelection) switch ( selections.size() ) { case 0 -> null; case 1 -> selections.get( 0 ).getSelectableNode(); @@ -438,7 +438,7 @@ public AbstractQuery having(List restrictions) { public void appendHqlString(StringBuilder hql, SqmRenderContext context) { if ( !cteStatements.isEmpty() ) { hql.append( "with " ); - for ( SqmCteStatement value : cteStatements.values() ) { + for ( var value : cteStatements.values() ) { value.appendHqlString( hql, context ); hql.append( ", " ); } @@ -479,7 +479,7 @@ public int cacheHashCode() { @SuppressWarnings("unchecked") protected Selection getResultSelection(Selection[] selections) { - final Class resultType = getResultType(); + final var resultType = getResultType(); if ( resultType == Object.class ) { return switch ( selections.length ) { case 0 -> throw new IllegalArgumentException( "Empty selections passed to criteria query typed as Object" ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcOperationQuery.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcOperationQuery.java index e111c2a17e7c..1c192f43dc3b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcOperationQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcOperationQuery.java @@ -90,9 +90,8 @@ public boolean isCompatibleWith(JdbcParameterBindings jdbcParameterBindings, Que return true; } - @SuppressWarnings("unchecked") static boolean equal(JdbcParameterBinding appliedBinding, JdbcParameterBinding binding, JavaType type) { return type.isInstance( appliedBinding.getBindValue() ) - && type.areEqual( (T) binding.getBindValue(), (T) appliedBinding.getBindValue() ); + && type.areEqual( type.cast( binding.getBindValue() ), type.cast( appliedBinding.getBindValue() ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java index 5b9901f60ca2..0cbefbe2b05d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java @@ -13,7 +13,6 @@ import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.InitializerData; -import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingResolution; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; import org.hibernate.sql.results.spi.RowReader; @@ -25,7 +24,6 @@ /** * @author Steve Ebersole */ -@SuppressWarnings("rawtypes") public class StandardRowReader implements RowReader { private final DomainResultAssembler[] resultAssemblers; private final Initializer[] resultInitializers; @@ -36,7 +34,7 @@ public class StandardRowReader implements RowReader { private final InitializerData[] sortedForResolveInstanceData; private final boolean hasCollectionInitializers; private final @Nullable RowTransformer rowTransformer; - private final Class domainResultJavaType; + private final @Nullable Class domainResultJavaType; private final ComponentType componentType; private final Class resultElementClass; @@ -44,7 +42,7 @@ public class StandardRowReader implements RowReader { public StandardRowReader( JdbcValuesMappingResolution jdbcValuesMappingResolution, RowTransformer rowTransformer, - Class domainResultJavaType) { + @Nullable Class domainResultJavaType) { this( jdbcValuesMappingResolution.getDomainResultAssemblers(), jdbcValuesMappingResolution.getResultInitializers(), @@ -63,7 +61,7 @@ public StandardRowReader( Initializer[] sortedForResolveInitializers, boolean hasCollectionInitializers, RowTransformer rowTransformer, - Class domainResultJavaType) { + @Nullable Class domainResultJavaType) { this.resultAssemblers = resultAssemblers; this.resultInitializers = (Initializer[]) resultInitializers; this.resultInitializersData = new InitializerData[resultInitializers.length]; @@ -72,11 +70,12 @@ public StandardRowReader( this.sortedForResolveInstance = (Initializer[]) sortedForResolveInitializers; this.sortedForResolveInstanceData = new InitializerData[sortedForResolveInstance.length]; this.hasCollectionInitializers = hasCollectionInitializers; - this.rowTransformer = rowTransformer == RowTransformerArrayImpl.instance() && resultAssemblers.length != 1 + this.rowTransformer = + rowTransformer == RowTransformerArrayImpl.instance() && resultAssemblers.length != 1 || rowTransformer == RowTransformerStandardImpl.instance() || rowTransformer == RowTransformerSingularReturnImpl.instance() && resultAssemblers.length == 1 - ? null - : rowTransformer; + ? null + : rowTransformer; this.domainResultJavaType = domainResultJavaType; if ( domainResultJavaType == null || domainResultJavaType == Object[].class @@ -100,8 +99,8 @@ public Class getDomainResultResultJavaType() { @Override public List<@Nullable JavaType> getResultJavaTypes() { - List> javaTypes = new ArrayList<>( resultAssemblers.length ); - for ( DomainResultAssembler resultAssembler : resultAssemblers ) { + final List> javaTypes = new ArrayList<>( resultAssemblers.length ); + for ( var resultAssembler : resultAssemblers ) { javaTypes.add( resultAssembler.getAssembledJavaType() ); } return javaTypes; @@ -114,15 +113,18 @@ public int getInitializerCount() { @Override public @Nullable EntityKey resolveSingleResultEntityKey(RowProcessingState rowProcessingState) { - final EntityInitializer entityInitializer = resultInitializers.length == 0 - ? null - : resultInitializers[0].asEntityInitializer(); + final var entityInitializer = + resultInitializers.length == 0 + ? null + : resultInitializers[0].asEntityInitializer(); if ( entityInitializer == null ) { return null; } - final EntityKey entityKey = entityInitializer.resolveEntityKeyOnly( rowProcessingState ); - finishUpRow(); - return entityKey; + else { + final var entityKey = entityInitializer.resolveEntityKeyOnly( rowProcessingState ); + finishUpRow(); + return entityKey; + } } @Override @@ -133,34 +135,32 @@ public boolean hasCollectionInitializers() { @Override @AllowReflection public T readRow(RowProcessingState rowProcessingState) { - coordinateInitializers( rowProcessingState ); + coordinateInitializers(); + final T result = getResult( rowProcessingState ); + finishUpRow(); + return result; + } - final T result; + @SuppressWarnings("unchecked") + private T getResult(RowProcessingState rowProcessingState) { if ( componentType != ComponentType.OBJECT ) { - result = readPrimitiveRow( rowProcessingState ); + return (T) readPrimitiveRow( rowProcessingState ); + } + else if ( resultAssemblers.length == 1 && rowTransformer == null ) { + return (T) resultAssemblers[0].assemble( rowProcessingState ); } else { - if ( resultAssemblers.length == 1 && rowTransformer == null ) { - //noinspection unchecked - result = (T) resultAssemblers[0].assemble( rowProcessingState ); - } - else { - final Object[] resultRow = (Object[]) Array.newInstance( resultElementClass, resultAssemblers.length ); - for ( int i = 0; i < resultAssemblers.length; i++ ) { - resultRow[i] = resultAssemblers[i].assemble( rowProcessingState ); - } - //noinspection unchecked - result = rowTransformer == null - ? (T) resultRow - : rowTransformer.transformRow( resultRow ); + final var resultRow = (Object[]) Array.newInstance( resultElementClass, resultAssemblers.length ); + for ( int i = 0; i < resultAssemblers.length; i++ ) { + resultRow[i] = resultAssemblers[i].assemble( rowProcessingState ); } + return rowTransformer == null + ? (T) resultRow + : rowTransformer.transformRow( resultRow ); } - - finishUpRow(); - return result; } - private T readPrimitiveRow(RowProcessingState rowProcessingState) { + private Object readPrimitiveRow(RowProcessingState rowProcessingState) { // The following is ugly, but unfortunately necessary to not hurt performance. // This implementation was micro-benchmarked and discussed with Francesco Nigro, // who hinted that using this style instead of the reflective Array.getLength(), Array.set() @@ -171,61 +171,61 @@ private T readPrimitiveRow(RowProcessingState rowProcessingState) { for ( int i = 0; i < resultAssemblers.length; i++ ) { resultBooleanRow[i] = (boolean) resultAssemblers[i].assemble( rowProcessingState ); } - return (T) resultBooleanRow; + return resultBooleanRow; case BYTE: final byte[] resultByteRow = new byte[resultAssemblers.length]; for ( int i = 0; i < resultAssemblers.length; i++ ) { resultByteRow[i] = (byte) resultAssemblers[i].assemble( rowProcessingState ); } - return (T) resultByteRow; + return resultByteRow; case CHAR: final char[] resultCharRow = new char[resultAssemblers.length]; for ( int i = 0; i < resultAssemblers.length; i++ ) { resultCharRow[i] = (char) resultAssemblers[i].assemble( rowProcessingState ); } - return (T) resultCharRow; + return resultCharRow; case SHORT: final short[] resultShortRow = new short[resultAssemblers.length]; for ( int i = 0; i < resultAssemblers.length; i++ ) { resultShortRow[i] = (short) resultAssemblers[i].assemble( rowProcessingState ); } - return (T) resultShortRow; + return resultShortRow; case INT: final int[] resultIntRow = new int[resultAssemblers.length]; for ( int i = 0; i < resultAssemblers.length; i++ ) { resultIntRow[i] = (int) resultAssemblers[i].assemble( rowProcessingState ); } - return (T) resultIntRow; + return resultIntRow; case LONG: final long[] resultLongRow = new long[resultAssemblers.length]; for ( int i = 0; i < resultAssemblers.length; i++ ) { resultLongRow[i] = (long) resultAssemblers[i].assemble( rowProcessingState ); } - return (T) resultLongRow; + return resultLongRow; case FLOAT: final float[] resultFloatRow = new float[resultAssemblers.length]; for ( int i = 0; i < resultAssemblers.length; i++ ) { resultFloatRow[i] = (float) resultAssemblers[i].assemble( rowProcessingState ); } - return (T) resultFloatRow; + return resultFloatRow; case DOUBLE: final double[] resultDoubleRow = new double[resultAssemblers.length]; for ( int i = 0; i < resultAssemblers.length; i++ ) { resultDoubleRow[i] = (double) resultAssemblers[i].assemble( rowProcessingState ); } - return (T) resultDoubleRow; + return resultDoubleRow; default: throw new AssertionError( "Object should be handled specially" ); } } private void finishUpRow() { - for ( InitializerData data : initializersData ) { + for ( var data : initializersData ) { data.setState( Initializer.State.UNINITIALIZED ); } } - private void coordinateInitializers(RowProcessingState rowProcessingState) { + private void coordinateInitializers() { for ( int i = 0; i < resultInitializers.length; i++ ) { resultInitializers[i].resolveKey( resultInitializersData[i] ); } @@ -244,7 +244,7 @@ private void coordinateInitializers(RowProcessingState rowProcessingState) { @Override public void startLoading(RowProcessingState processingState) { for ( int i = 0; i < resultInitializers.length; i++ ) { - final Initializer initializer = resultInitializers[i]; + final var initializer = resultInitializers[i]; initializer.startLoading( processingState ); resultInitializersData[i] = initializer.getData( processingState ); } @@ -305,7 +305,9 @@ else if ( resultType == float[].class) { else if ( resultType == double[].class) { return DOUBLE; } - return OBJECT; + else { + return OBJECT; + } } public Class getComponentType() { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleImpl.java index 2409c6c0d353..f11ce68c3f5c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleImpl.java @@ -10,7 +10,8 @@ import jakarta.persistence.Tuple; import jakarta.persistence.TupleElement; -import static org.hibernate.internal.util.type.PrimitiveWrapperHelper.getDescriptorByPrimitiveType; +import static org.hibernate.internal.util.type.PrimitiveWrapperHelper.cast; +import static org.hibernate.internal.util.type.PrimitiveWrapperHelper.isInstance; /** * Implementation of the JPA Tuple contract @@ -27,7 +28,6 @@ public TupleImpl(TupleMetadata tupleMetadata, Object[] row) { } @Override - @SuppressWarnings("unchecked") public X get(TupleElement tupleElement) { final Integer index = tupleMetadata.get( tupleElement ); if ( index == null ) { @@ -36,15 +36,14 @@ public X get(TupleElement tupleElement) { ); } // index should be "in range" by nature of size check in ctor - return (X) row[index]; + return cast( tupleElement.getJavaType(), row[index] ); } @Override - @SuppressWarnings("unchecked") public X get(String alias, Class type) { final Object untyped = get( alias ); if ( untyped != null ) { - if ( !elementTypeMatches( type, untyped ) ) { + if ( !isInstance( type, untyped ) ) { throw new IllegalArgumentException( String.format( "Requested tuple value [alias=%s, value=%s] cannot be assigned to requested type [%s]", @@ -55,12 +54,12 @@ public X get(String alias, Class type) { ); } } - return (X) untyped; + return cast( type, untyped ); } @Override public Object get(String alias) { - Integer index = tupleMetadata.get( alias ); + final Integer index = tupleMetadata.get( alias ); if ( index == null ) { throw new IllegalArgumentException( "Given alias [" + alias + "] did not correspond to an element in the result tuple" @@ -71,10 +70,9 @@ public Object get(String alias) { } @Override - @SuppressWarnings("unchecked") public X get(int i, Class type) { final Object result = get( i ); - if ( result != null && !elementTypeMatches( type, result ) ) { + if ( result != null && !isInstance( type, result ) ) { throw new IllegalArgumentException( String.format( "Requested tuple value [index=%s, realType=%s] cannot be assigned to requested type [%s]", @@ -84,7 +82,7 @@ public X get(int i, Class type) { ) ); } - return (X) result; + return cast( type, result ); } @Override @@ -97,11 +95,6 @@ public Object get(int i) { return row[i]; } - private boolean elementTypeMatches(Class type, Object untyped) { - return type.isInstance( untyped ) - || type.isPrimitive() && getDescriptorByPrimitiveType( type ).getWrapperClass().isInstance( untyped ); - } - @Override public Object[] toArray() { return row; diff --git a/hibernate-core/src/main/java/org/hibernate/usertype/UserType.java b/hibernate-core/src/main/java/org/hibernate/usertype/UserType.java index 96757961a1bf..8ecc1dd8c3af 100644 --- a/hibernate-core/src/main/java/org/hibernate/usertype/UserType.java +++ b/hibernate-core/src/main/java/org/hibernate/usertype/UserType.java @@ -448,8 +448,9 @@ default Serializable disassemble(J value) { * @see org.hibernate.Cache */ default J assemble(Serializable cached, Object owner) { - if ( returnedClass().isInstance( cached) ) { - return deepCopy( (J) cached ); + final var returnedClass = returnedClass(); + if ( returnedClass.isInstance( cached ) ) { + return deepCopy( returnedClass.cast( cached ) ); } else { throw new UnsupportedOperationException( "User-defined type '" diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/EnumType.java b/hibernate-core/src/test/java/org/hibernate/orm/test/EnumType.java index 338c44fde74f..d04760f2a4f0 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/EnumType.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/EnumType.java @@ -175,11 +175,10 @@ private boolean isNationalized(ParameterType reader) { || reader!=null && getAnnotation( reader.getAnnotationsMethod(), Nationalized.class ) != null; } - @SuppressWarnings("unchecked") private A getAnnotation(Annotation[] annotations, Class annotationType) { for ( Annotation annotation : annotations ) { if ( annotationType.isInstance( annotation ) ) { - return (A) annotation; + return annotationType.cast( annotation ); } } return null; From 265a5a185dd651934d00fe55ee7702668fb85e0a Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 6 Dec 2025 23:45:44 +0100 Subject: [PATCH 039/181] slight simplification to SessionImpl.setProperty() --- .../org/hibernate/internal/SessionImpl.java | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 9f01e1fe356d..47f6e75468e8 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -2582,24 +2582,22 @@ public LockModeType getLockMode(Object entity) { @Override public void setProperty(String propertyName, Object value) { checkOpen(); - if ( value instanceof Serializable ) { - if ( propertyName != null ) { - // store property for future reference - if ( properties == null ) { - properties = getInitialProperties(); - } - properties.put( propertyName, value ); - // now actually update the setting if - // it's one that affects this Session - interpretProperty( propertyName, value ); - } - else { - SESSION_LOGGER.nullPropertyKey(); - } + if ( propertyName == null ) { + SESSION_LOGGER.nullPropertyKey(); } - else { + else if ( !(value instanceof Serializable) ) { SESSION_LOGGER.nonSerializableProperty( propertyName ); } + else { + // store property for future reference + if ( properties == null ) { + properties = getInitialProperties(); + } + properties.put( propertyName, value ); + // now actually update the setting if + // it's one that affects this Session + interpretProperty( propertyName, value ); + } } private void interpretProperty(String propertyName, Object value) { From e4f605d09697e3781331b09f0ea8eed04d8286b2 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 7 Dec 2025 00:27:59 +0100 Subject: [PATCH 040/181] attempt to make checker happy --- .../org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java | 2 +- .../org/hibernate/sql/results/internal/StandardRowReader.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java index 937e489afd3a..76f83749a12a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java @@ -146,7 +146,7 @@ protected void copyTo(AbstractSqmFrom target, SqmCopyContext context) { if ( treats != null ) { final ArrayList> newTreats = new ArrayList<>( treats.size() ); - for ( var treat : treats ) { + for ( SqmTreatedFrom treat : treats ) { newTreats.add( treat.copy( context ) ); } target.treats = newTreats; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java index 0cbefbe2b05d..4499d498a896 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java @@ -133,7 +133,6 @@ public boolean hasCollectionInitializers() { } @Override - @AllowReflection public T readRow(RowProcessingState rowProcessingState) { coordinateInitializers(); final T result = getResult( rowProcessingState ); @@ -141,6 +140,7 @@ public T readRow(RowProcessingState rowProcessingState) { return result; } + @AllowReflection @SuppressWarnings("unchecked") private T getResult(RowProcessingState rowProcessingState) { if ( componentType != ComponentType.OBJECT ) { From acfcfcca1fa222e3db08412ada2b84e15324bd65 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 7 Dec 2025 09:43:45 +0100 Subject: [PATCH 041/181] hack around an issue with mapping arrays of Date as SQL ARRAYs I don't know why we even need to be doing this. We deprecated that stuff. But we have one single test which is relying on this, so perhaps users are using it too. --- .../type/AbstractStandardBasicType.java | 15 ++---- .../org/hibernate/type/BasicArrayType.java | 28 ++++++++++- .../java/AbstractArrayJavaType.java | 12 +++++ .../type/descriptor/java/ArrayJavaType.java | 48 +++++++++++++++---- .../array/MySqlArrayOfTimestampsTest.java | 5 -- 5 files changed, 84 insertions(+), 24 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java b/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java index 9c6434711ef7..f3ace5a14584 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java @@ -14,7 +14,6 @@ import java.util.Map; import org.hibernate.Hibernate; -import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -268,21 +267,17 @@ public final boolean isMutable() { } @Override - public final Object deepCopy(Object value, SessionFactoryImplementor factory) { - return deepCopy( javaType.cast( value ) ); - } - - protected final T deepCopy(T value) { - return getMutabilityPlan().deepCopy( value ); + public Object deepCopy(Object value, SessionFactoryImplementor factory) { + return getMutabilityPlan().deepCopy( javaType.cast( value ) ); } @Override - public final Serializable disassemble(Object value, SharedSessionContractImplementor session, Object owner) throws HibernateException { - return getMutabilityPlan().disassemble( javaType.cast( value ) , session ); + public final Serializable disassemble(Object value, SharedSessionContractImplementor session, Object owner) { + return getMutabilityPlan().disassemble( javaType.cast( value ), session ); } @Override - public final Object assemble(Serializable cached, SharedSessionContractImplementor session, Object owner) throws HibernateException { + public final Object assemble(Serializable cached, SharedSessionContractImplementor session, Object owner) { return getMutabilityPlan().assemble( cached, session ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/BasicArrayType.java b/hibernate-core/src/main/java/org/hibernate/type/BasicArrayType.java index 963bb7b436c2..2d49dcad048e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/BasicArrayType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/BasicArrayType.java @@ -6,6 +6,8 @@ import java.util.Objects; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.type.descriptor.java.AbstractArrayJavaType; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; @@ -16,17 +18,19 @@ * @author Jordan Gigov * @author Christian Beikov */ -public class BasicArrayType +public final class BasicArrayType extends AbstractSingleColumnStandardBasicType implements AdjustableBasicType, BasicPluralType { private final BasicType baseDescriptor; private final String name; + private final AbstractArrayJavaType arrayTypeDescriptor; public BasicArrayType(BasicType baseDescriptor, JdbcType arrayJdbcType, JavaType arrayTypeDescriptor) { super( arrayJdbcType, arrayTypeDescriptor ); this.baseDescriptor = baseDescriptor; this.name = determineArrayTypeName( baseDescriptor ); + this.arrayTypeDescriptor = (AbstractArrayJavaType) arrayTypeDescriptor; } static String determineElementTypeName(BasicType baseDescriptor) { @@ -75,4 +79,26 @@ public boolean equals(Object object) { public int hashCode() { return baseDescriptor.hashCode(); } + + // Methods required to support Horrible hack around the fact + // that java.sql.Timestamps in an array can be represented as + // instances of java.util.Date (Why do we even allow this?) + + @Override + public boolean isEqual(Object one, Object another) { + if ( one == another ) { + return true; + } + else if ( one == null || another == null ) { + return false; + } + else { + return arrayTypeDescriptor.isEqual( one, another ); + } + } + + @Override + public Object deepCopy(Object value, SessionFactoryImplementor factory) { + return arrayTypeDescriptor.deepCopy( value ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java index d7d1207db2bd..dcb9ac7a03fa 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java @@ -19,6 +19,7 @@ import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; import org.hibernate.type.spi.TypeConfiguration; + import static java.lang.reflect.Array.newInstance; @AllowReflection @@ -123,4 +124,15 @@ BasicType resolveType( () -> new BasicArrayType<>( elementType, arrayJdbcType, arrayJavaType ) ); } + // Methods required to support Horrible hack around the fact + // that java.sql.Timestamps in an array can be represented as + // instances of java.util.Date (Why do we even allow this?) + + public T deepCopy(Object value) { + return getMutabilityPlan().deepCopy( cast( value ) ); + } + + public boolean isEqual(Object one, Object another) { + return areEqual( cast( one ), cast( another) ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java index 323d9312f228..a4a7098f0ae1 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java @@ -111,7 +111,7 @@ public String extractLoggableRepresentation(T[] value) { } @Override - public boolean areEqual(T[] one, T[] another) { + public boolean areEqual(Object[] one, Object[] another) { if ( one == null && another == null ) { return true; } @@ -123,7 +123,13 @@ public boolean areEqual(T[] one, T[] another) { } int l = one.length; for ( int i = 0; i < l; i++ ) { - if ( !getElementJavaType().areEqual( one[i], another[i] )) { + final var elementJavaType = getElementJavaType(); + if ( !elementJavaType.areEqual( + // Horrible hack around the fact that java.sql.Timestamps + // can be represented as instances of java.util.Date + // (Why do we even allow this? We deprecated java.sql stuff!) + elementJavaType.coerce( one[i], null ), + elementJavaType.coerce( another[i], null ) )) { return false; } } @@ -376,15 +382,36 @@ private T[] fromBytes(byte[] bytes) { } } + // Methods required to support Horrible hack around the fact + // that java.sql.Timestamps in an array can be represented as + // instances of java.util.Date (Why do we even allow this?) + + @Override + public T[] deepCopy(Object value) { + final var mutabilityPlan = + (ArrayMutabilityPlan) + super.getMutabilityPlan(); + return mutabilityPlan.deepCopy( (Object[]) value ); + } + + @Override + public boolean isEqual(Object one, Object another) { + return areEqual( (Object[]) one, (Object[]) another ); + } + @AllowReflection private static class ArrayMutabilityPlan implements MutabilityPlan { private final Class componentClass; private final MutabilityPlan componentPlan; + private final Class arrayClass; + private final JavaType baseDescriptor; public ArrayMutabilityPlan(JavaType baseDescriptor) { + this.baseDescriptor = baseDescriptor; this.componentClass = baseDescriptor.getJavaTypeClass(); this.componentPlan = baseDescriptor.getMutabilityPlan(); + this.arrayClass = arrayClass( componentClass ); } @Override @@ -393,16 +420,21 @@ public boolean isMutable() { } @Override - public T[] deepCopy(T[] value) { + public T[] deepCopy(Object[] value) { if ( value == null ) { return null; } - //noinspection unchecked - final T[] copy = (T[]) newInstance( componentClass, value.length ); - for ( int i = 0; i < value.length; i ++ ) { - copy[ i ] = componentPlan.deepCopy( value[ i ] ); + else { + final var copy = arrayClass.cast( newInstance( componentClass, value.length ) ); + for ( int i = 0; i < value.length; i++ ) { + copy[i] = componentPlan.deepCopy( + // Horrible hack around the fact that java.sql.Timestamps + // can be represented as instances of java.util.Date + // (Why do we even allow this? We deprecated java.sql stuff!) + baseDescriptor.coerce( value[i], null ) ); + } + return copy; } - return copy; } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/array/MySqlArrayOfTimestampsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/array/MySqlArrayOfTimestampsTest.java index 4829c0608978..b996924b5c67 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/array/MySqlArrayOfTimestampsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/array/MySqlArrayOfTimestampsTest.java @@ -22,7 +22,6 @@ import org.junit.jupiter.api.Test; import java.sql.Timestamp; -import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.util.Calendar; @@ -109,10 +108,6 @@ public void testDate(SessionFactoryScope scope) { } ); } - private static final LocalDateTime SUMMER = LocalDate.of( 2024, 6, 20 ).atStartOfDay(); - private static final LocalDateTime WINTER = LocalDate.of( 2023, 12, 22 ).atStartOfDay(); - private static final LocalDate EPOCH = LocalDate.of( 1970, Month.JANUARY, 1 ); - private static final TimeZone[] TEST_TIME_ZONES = Stream.of( "Africa/Monrovia", "Europe/Zagreb", From c2272a7a2e37f4f5c7bb6def81af8b2ef11de479 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 7 Dec 2025 09:44:21 +0100 Subject: [PATCH 042/181] misc code cleanups --- .../type/AbstractStandardBasicType.java | 2 +- .../org/hibernate/type/BasicArrayType.java | 15 ++++--- .../java/JdbcTimestampJavaType.java | 44 ++++++++----------- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java b/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java index f3ace5a14584..4dca9e44c747 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java @@ -93,7 +93,7 @@ public T fromString(CharSequence string) { } protected MutabilityPlan getMutabilityPlan() { - return this.mutabilityPlan; + return mutabilityPlan; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/BasicArrayType.java b/hibernate-core/src/main/java/org/hibernate/type/BasicArrayType.java index 2d49dcad048e..29bda587fe0c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/BasicArrayType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/BasicArrayType.java @@ -12,6 +12,8 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import static java.lang.Character.toUpperCase; + /** * A type that maps between {@link java.sql.Types#ARRAY ARRAY} and {@code T[]} * @@ -37,7 +39,8 @@ static String determineElementTypeName(BasicType baseDescriptor) { final String elementName = baseDescriptor.getName(); return switch ( elementName ) { case "boolean", "byte", "char", "short", "int", "long", "float", "double" -> - Character.toUpperCase( elementName.charAt( 0 ) ) + elementName.substring( 1 ); + toUpperCase( elementName.charAt( 0 ) ) + + elementName.substring( 1 ); default -> elementName; }; } @@ -63,16 +66,18 @@ protected boolean registerUnderJavaType() { @Override public BasicType resolveIndicatedType(JdbcTypeIndicators indicators, JavaType domainJtd) { - // TODO: maybe fallback to some encoding by default if the DB doesn't support arrays natively? - // also, maybe move that logic into the ArrayJdbcType + // TODO: maybe fall back to some encoding by default if + // the database doesn't support arrays natively? + // also, maybe move that logic into the ArrayJdbcType //noinspection unchecked return (BasicType) this; } @Override public boolean equals(Object object) { - return object == this || object.getClass() == BasicArrayType.class - && Objects.equals( baseDescriptor, ( (BasicArrayType) object ).baseDescriptor ); + return object == this + || object instanceof BasicArrayType arrayType // no subtypes + && Objects.equals( baseDescriptor, arrayType.baseDescriptor ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java index 2cde56adea3f..2b8f61a016e1 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java @@ -85,32 +85,26 @@ public boolean areEqual(Date one, Date another) { if ( one == another ) { return true; } - if ( one == null || another == null) { + else if ( one == null || another == null) { return false; } - - long t1 = one.getTime(); - long t2 = another.getTime(); - - boolean oneIsTimestamp = one instanceof Timestamp; - boolean anotherIsTimestamp = another instanceof Timestamp; - - int n1 = oneIsTimestamp ? ( (Timestamp) one ).getNanos() : 0; - int n2 = anotherIsTimestamp ? ( (Timestamp) another ).getNanos() : 0; - - if ( t1 != t2 ) { - return false; - } - - if ( oneIsTimestamp && anotherIsTimestamp ) { - // both are Timestamps - int nn1 = n1 % 1000000; - int nn2 = n2 % 1000000; - return nn1 == nn2; - } else { - // at least one is a plain old Date - return true; + final long t1 = one.getTime(); + final long t2 = another.getTime(); + if ( t1 != t2 ) { + return false; + } + else if ( one instanceof Timestamp ts1 + && another instanceof Timestamp ts2 ) { + // both are Timestamps + final int nn1 = ts1.getNanos() % 1000000; + final int nn2 = ts2.getNanos() % 1000000; + return nn1 == nn2; + } + else { + // at least one is a plain old Date + return true; + } } } @@ -132,8 +126,8 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { } if ( Timestamp.class.isAssignableFrom( type ) ) { - return value instanceof Timestamp - ? (Timestamp) value + return value instanceof Timestamp timestamp + ? timestamp : new Timestamp( value.getTime() ); } From 78da9ff345fb6c760bd03ce537c8d47c2ff19cdd Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 7 Dec 2025 11:08:09 +0100 Subject: [PATCH 043/181] fix up a bunch more unchecked casts --- .../ValueBindJpaCriteriaParameter.java | 39 +++++--- .../converter/internal/ArrayConverter.java | 68 ++++++------- .../java/AbstractArrayJavaType.java | 8 +- .../type/descriptor/java/ArrayJavaType.java | 97 +++++++++++-------- .../descriptor/java/ArrayMutabilityPlan.java | 25 ++--- .../type/descriptor/java/EnumJavaType.java | 3 +- 6 files changed, 131 insertions(+), 109 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/ValueBindJpaCriteriaParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/ValueBindJpaCriteriaParameter.java index 6562bde5dd45..89736c09f9c2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/ValueBindJpaCriteriaParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/ValueBindJpaCriteriaParameter.java @@ -40,7 +40,7 @@ private ValueBindJpaCriteriaParameter(ValueBindJpaCriteriaParameter original) @Override public ValueBindJpaCriteriaParameter copy(SqmCopyContext context) { - final ValueBindJpaCriteriaParameter existing = context.getCopy( this ); + final var existing = context.getCopy( this ); return existing != null ? existing : context.registerCopy( this, new ValueBindJpaCriteriaParameter<>( this ) ); @@ -59,26 +59,31 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { // but for equals/hashCode, use equals/hashCode of the underlying value, if available, from the nodes JavaType @Override - public final boolean equals(@Nullable Object o) { - if ( this == o ) { + public final boolean equals(@Nullable Object object) { + if ( this == object ) { return true; } - if ( o instanceof ValueBindJpaCriteriaParameter that ) { - if ( value == null ) { - return that.value == null && Objects.equals( getNodeType(), that.getNodeType() ); + else if ( object instanceof ValueBindJpaCriteriaParameter that ) { + if ( this.value == null || that.value == null ) { + return this.value == that.value + && Objects.equals( this.getNodeType(), that.getNodeType() ); } - final var javaType = getJavaTypeDescriptor(); - if ( that.value != null ) { - if ( javaType != null ) { - //noinspection unchecked - return javaType.equals( that.getJavaTypeDescriptor() ) && javaType.areEqual( value, (T) that.value ); + else { + final var thisJavaType = getJavaTypeDescriptor(); + final var thatJavaType = that.getJavaTypeDescriptor(); + if ( thisJavaType == null || thatJavaType == null ) { + return thisJavaType == thatJavaType + && this.value.equals( that.value ); } else { - return that.getJavaTypeDescriptor() == null && value.equals( that.value ); + return thisJavaType.equals( thatJavaType ) + && thisJavaType.areEqual( value, thisJavaType.cast( that.value ) ); } } } - return false; + else { + return false; + } } @Override @@ -86,7 +91,11 @@ public int hashCode() { if ( value == null ) { return 0; } - final var javaType = getJavaTypeDescriptor(); - return javaType == null ? value.hashCode() : javaType.extractHashCode( value ); + else { + final var javaType = getJavaTypeDescriptor(); + return javaType == null + ? value.hashCode() + : javaType.extractHashCode( value ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/ArrayConverter.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/ArrayConverter.java index 0b7076ee3f47..715406a406df 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/ArrayConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/ArrayConverter.java @@ -4,93 +4,87 @@ */ package org.hibernate.type.descriptor.converter.internal; -import java.lang.reflect.Array; - import org.hibernate.internal.build.AllowReflection; +import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; import org.hibernate.type.descriptor.java.JavaType; +import static java.lang.reflect.Array.newInstance; +import static org.hibernate.internal.util.ReflectHelper.arrayClass; + /** * Given a {@link BasicValueConverter} for array elements, handles conversion * to and from an array of the converted element type. * * @param the unconverted element type * @param the converted element type - * @param the unconverted array type - * @param the converted array type + * @param the unconverted array type (same as {@code E[]}) */ @AllowReflection -public class ArrayConverter implements BasicValueConverter { +public class ArrayConverter implements BasicValueConverter { private final BasicValueConverter elementConverter; private final JavaType domainJavaType; - private final JavaType relationalJavaType; + private final JavaType relationalJavaType; + private final Class arrayClass; public ArrayConverter( BasicValueConverter elementConverter, JavaType domainJavaType, - JavaType relationalJavaType) { + JavaType relationalJavaType, + BasicType elementType) { this.elementConverter = elementConverter; this.domainJavaType = domainJavaType; this.relationalJavaType = relationalJavaType; + this.arrayClass = arrayClass( elementType.getJavaType() ); } @Override - public T toDomainValue(S relationalForm) { + public T toDomainValue(F[] relationalForm) { if ( relationalForm == null ) { return null; } else { - final Class elementClass = elementConverter.getDomainJavaType().getJavaTypeClass(); - if ( relationalForm.getClass().getComponentType() == elementClass) { - //noinspection unchecked - return (T) relationalForm; - } - else { - //noinspection unchecked - return convertTo( (F[]) relationalForm, elementClass ); - } + final var elementClass = + elementConverter.getDomainJavaType().getJavaTypeClass(); + return relationalForm.getClass().getComponentType() == elementClass + ? domainJavaType.cast( relationalForm ) + : convertTo( relationalJavaType.cast( relationalForm ), elementClass ); } } private T convertTo(F[] relationalArray, Class elementClass) { //TODO: the following implementation only handles conversion between non-primitive arrays! - //noinspection unchecked - final E[] domainArray = (E[]) Array.newInstance( elementClass, relationalArray.length ); + final var domainArray = + arrayClass.cast( newInstance( elementClass, relationalArray.length ) ); for ( int i = 0; i < relationalArray.length; i++ ) { domainArray[i] = elementConverter.toDomainValue( relationalArray[i] ); } - //noinspection unchecked - return (T) domainArray; + return domainJavaType.cast( domainArray ); } @Override - public S toRelationalValue(T domainForm) { + public F[] toRelationalValue(T domainForm) { if ( domainForm == null ) { return null; } else { - final Class elementClass = elementConverter.getRelationalJavaType().getJavaTypeClass(); - if ( domainForm.getClass().getComponentType() == elementClass) { - //noinspection unchecked - return (S) domainForm; - } - else { - //noinspection unchecked - return convertFrom((E[]) domainForm, elementClass); - } + final Class elementClass = + elementConverter.getRelationalJavaType().getJavaTypeClass(); + return domainForm.getClass().getComponentType() == elementClass + ? relationalJavaType.cast( domainForm ) + : convertFrom( arrayClass.cast( domainForm ), elementClass ); } } - private S convertFrom(E[] domainArray, Class elementClass) { + private F[] convertFrom(E[] domainArray, Class elementClass) { //TODO: the following implementation only handles conversion between non-primitive arrays! - //noinspection unchecked - final F[] relationalArray = (F[]) Array.newInstance( elementClass, domainArray.length ); + final var relationalArray = + relationalJavaType.cast( newInstance( elementClass, domainArray.length ) ); for ( int i = 0; i < domainArray.length; i++ ) { relationalArray[i] = elementConverter.toRelationalValue( domainArray[i] ); } - //noinspection unchecked - return (S) relationalArray; + return relationalArray; } @Override @@ -99,7 +93,7 @@ public JavaType getDomainJavaType() { } @Override - public JavaType getRelationalJavaType() { + public JavaType getRelationalJavaType() { return relationalJavaType; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java index dcb9ac7a03fa..3a2fb269d8d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java @@ -20,7 +20,7 @@ import org.hibernate.type.spi.TypeConfiguration; -import static java.lang.reflect.Array.newInstance; +import static org.hibernate.internal.util.ReflectHelper.arrayClass; @AllowReflection public abstract class AbstractArrayJavaType extends AbstractClassJavaType @@ -98,8 +98,8 @@ BasicType createTypeUsingConverter( ColumnTypeInformation columnTypeInformation, JdbcTypeIndicators indicators, BasicValueConverter valueConverter) { - final var convertedElementClass = valueConverter.getRelationalJavaType().getJavaTypeClass(); - final var convertedArrayClass = newInstance( convertedElementClass, 0 ).getClass(); + final var convertedArrayClass = + arrayClass( valueConverter.getRelationalJavaType().getJavaTypeClass() ); final var relationalJavaType = typeConfiguration.getJavaTypeRegistry() .resolveDescriptor( convertedArrayClass ); @@ -107,7 +107,7 @@ BasicType createTypeUsingConverter( elementType, arrayJdbcType( typeConfiguration, elementType, columnTypeInformation, indicators ), this, - new ArrayConverter<>( valueConverter, this, relationalJavaType ) + new ArrayConverter<>( valueConverter, this, relationalJavaType, elementType ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java index a4a7098f0ae1..8010b895a3b4 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java @@ -299,52 +299,71 @@ public T[] wrap(X value, WrapperOptions options) { } } - final var elementJavaType = getElementJavaType(); - if ( value instanceof Object[] raw ) { - final var componentClass = elementJavaType.getJavaTypeClass(); - //noinspection unchecked - final var wrapped = (T[]) newInstance( componentClass, raw.length ); - if ( componentClass.isAssignableFrom( value.getClass().getComponentType() ) ) { - for (int i = 0; i < raw.length; i++) { - //noinspection unchecked - wrapped[i] = (T) raw[i]; - } - } - else { - for ( int i = 0; i < raw.length; i++ ) { - wrapped[i] = elementJavaType.wrap( raw[i], options ); - } - } - return wrapped; + if ( value instanceof Object[] array ) { + return wrapObjectArray( value, array, options ); } else if ( value instanceof byte[] bytes ) { return fromBytes( bytes ); } else if ( value instanceof BinaryStream binaryStream ) { - // When the value is a BinaryStream, this is a deserialization request + // When the value is a BinaryStream, + // this is a deserialization request return fromBytes( binaryStream.getBytes() ); } - else if ( elementJavaType.isInstance( value ) ) { - // Support binding a single element as parameter value - //noinspection unchecked - final var wrapped = (T[]) newInstance( elementJavaType.getJavaTypeClass(), 1 ); - //noinspection unchecked - wrapped[0] = (T) value; - return wrapped; - } else if ( value instanceof Collection collection ) { - //noinspection unchecked - final var wrapped = (T[]) newInstance( elementJavaType.getJavaTypeClass(), collection.size() ); - int i = 0; - for ( Object e : collection ) { - wrapped[i++] = elementJavaType.wrap( e, options ); - } - return wrapped; + return wrapCollection( collection, options ); + } + else if ( getElementJavaType().isInstance( value ) ) { + // Support binding a single element as a parameter value + return wrapSingleElement( value, options ); } throw unknownWrap( value.getClass() ); } + private T[] wrapCollection(Collection collection, WrapperOptions options) { + final var arrayClass = getJavaTypeClass(); + final var elementJavaType = getElementJavaType(); + final var wrapped = newArray( arrayClass, elementJavaType, collection.size() ); + int i = 0; + for ( Object element : collection ) { + wrapped[i++] = elementJavaType.wrap( element, options ); + } + return wrapped; + } + + private < X> T[] wrapSingleElement(X value, WrapperOptions options) { + final var arrayClass = getJavaTypeClass(); + final var elementJavaType = getElementJavaType(); + final var wrapped = newArray( arrayClass, elementJavaType, 1 ); + wrapped[0] = elementJavaType.wrap( value, options ); + return wrapped; + } + + private T[] wrapObjectArray(X value, Object[] array, WrapperOptions options) { + final var arrayClass = getJavaTypeClass(); + final var elementJavaType = getElementJavaType(); + final var wrapped = newArray( arrayClass, elementJavaType, array.length ); + // I suppose this code was there as an optimization, + // but it doesn't really look necessary to me +// if ( elementJavaType.getJavaTypeClass() +// .isAssignableFrom( value.getClass().getComponentType() ) ) { +// for ( int i = 0; i < array.length; i++) { +// wrapped[i] = elementJavaType.cast( array[i] ); +// } +// } +// else { + for ( int i = 0; i < array.length; i++ ) { + wrapped[i] = elementJavaType.wrap( array[i], options ); + } +// } + return wrapped; + } + + private static T[] newArray(Class arrayClass, JavaType elementJavaType, int length) { + return arrayClass.cast( newInstance( elementJavaType.getJavaTypeClass(), length ) ); + } + private static byte[] toBytes(T[] array) { if ( array.getClass().getComponentType().isEnum() ) { final byte[] bytes = new byte[array.length]; @@ -364,21 +383,19 @@ private static byte[] toBytes(T[] array) { private T[] fromBytes(byte[] bytes) { final var elementClass = getElementJavaType().getJavaTypeClass(); + final var arrayClass = getJavaTypeClass(); if ( elementClass.isEnum() ) { - final T[] enumConstants = elementClass.getEnumConstants(); - final var array = (Object[]) newInstance( elementClass, bytes.length ); + final var enumConstants = elementClass.getEnumConstants(); + final var array = newArray( arrayClass, getElementJavaType(), bytes.length ); for (int i = 0; i < bytes.length; i++ ) { // null enum value was encoded as -1 array[i] = bytes[i] == -1 ? null : enumConstants[bytes[i]]; } - //noinspection unchecked - return (T[]) array; - + return array; } else { // When the value is a byte[], this is a deserialization request - //noinspection unchecked - return (T[]) SerializationHelper.deserialize(bytes); + return arrayClass.cast( SerializationHelper.deserialize( bytes ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java index 0298aff86f0d..6dddd6a5dcb0 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java @@ -3,34 +3,37 @@ * Copyright Red Hat Inc. and Hibernate Authors */ package org.hibernate.type.descriptor.java; + import org.hibernate.internal.build.AllowReflection; -import java.lang.reflect.Array; +import static java.lang.reflect.Array.getLength; +import static java.lang.reflect.Array.newInstance; /** - * A mutability plan for arrays. Specifically arrays of immutable element type; since the elements themselves - * are immutable, a shallow copy is enough. + * A mutability plan for arrays. Specifically arrays of immutable element type; + * since the elements themselves are immutable, a shallow copy is enough. * * @author Steve Ebersole * * @deprecated Use {@link ImmutableObjectArrayMutabilityPlan#get()} for object arrays, - * or implement a dedicated mutability plan for primitive arrays - * (see for example {@link ShortPrimitiveArrayJavaType}'s mutability plan). + * or implement a dedicated mutability plan for primitive arrays + * (see for example {@link ShortPrimitiveArrayJavaType}'s mutability plan). */ -@Deprecated +@Deprecated(forRemoval = true, since = "7.0") public class ArrayMutabilityPlan extends MutableMutabilityPlan { public static final ArrayMutabilityPlan INSTANCE = new ArrayMutabilityPlan(); @SuppressWarnings({ "unchecked", "SuspiciousSystemArraycopy" }) @AllowReflection public T deepCopyNotNull(T value) { - if ( ! value.getClass().isArray() ) { + final var valueClass = value.getClass(); + if ( !valueClass.isArray() ) { // ugh! cannot find a way to properly define the type signature here - throw new IllegalArgumentException( "Value was not an array [" + value.getClass().getName() + "]" ); + throw new IllegalArgumentException( "Value was not an array [" + valueClass.getName() + "]" ); } - final int length = Array.getLength( value ); - T copy = (T) Array.newInstance( value.getClass().getComponentType(), length ); + final int length = getLength( value ); + final Object copy = newInstance( valueClass.getComponentType(), length ); System.arraycopy( value, 0, copy, 0, length ); - return copy; + return (T) copy; } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/EnumJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/EnumJavaType.java index 4e33a9875fb8..60241c28889f 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/EnumJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/EnumJavaType.java @@ -116,7 +116,6 @@ else if ( Byte.class.equals( type ) ) { } @Override - @SuppressWarnings("unchecked") public T wrap(X value, WrapperOptions options) { if ( value == null ) { return null; @@ -140,7 +139,7 @@ else if ( value instanceof Number number ) { return fromLong( number.longValue() ); } else { - return (T) value; + return cast( value ); } } From 74d02951ce286debaeba670030f3726dff5ba44d Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 7 Dec 2025 11:21:28 +0100 Subject: [PATCH 044/181] work around a design problem (possibly a bug) affecting hibernate-vector --- .../org/hibernate/type/BasicArrayType.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/BasicArrayType.java b/hibernate-core/src/main/java/org/hibernate/type/BasicArrayType.java index 29bda587fe0c..13502e29806b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/BasicArrayType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/BasicArrayType.java @@ -32,7 +32,13 @@ public BasicArrayType(BasicType baseDescriptor, JdbcType arrayJdbcType, JavaT super( arrayJdbcType, arrayTypeDescriptor ); this.baseDescriptor = baseDescriptor; this.name = determineArrayTypeName( baseDescriptor ); - this.arrayTypeDescriptor = (AbstractArrayJavaType) arrayTypeDescriptor; + this.arrayTypeDescriptor = + arrayTypeDescriptor instanceof AbstractArrayJavaType arrayJavaType + ? arrayJavaType + // this only happens with contributions from hibernate-vector + // because it passes in a PrimitiveByteArrayJavaType which is + // not an AbstractArrayJavaType (this might be a bug) + : null; } static String determineElementTypeName(BasicType baseDescriptor) { @@ -91,7 +97,11 @@ public int hashCode() { @Override public boolean isEqual(Object one, Object another) { - if ( one == another ) { + if ( arrayTypeDescriptor == null ) { + // for hibernate-vector + return super.isEqual( one, another ); + } + else if ( one == another ) { return true; } else if ( one == null || another == null ) { @@ -104,6 +114,8 @@ else if ( one == null || another == null ) { @Override public Object deepCopy(Object value, SessionFactoryImplementor factory) { - return arrayTypeDescriptor.deepCopy( value ); + return arrayTypeDescriptor == null + ? super.deepCopy( value, factory ) // for hibernate-vector + : arrayTypeDescriptor.deepCopy( value ); } } From 0cdbc4d8899bef8957382c6c9817433fcd68701c Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 7 Dec 2025 12:31:57 +0100 Subject: [PATCH 045/181] deprecate the unsafe methods of JpaMetamodel and fix unchecked casts in the impl --- .../metamodel/model/domain/JpaMetamodel.java | 12 +++++ .../domain/internal/JpaMetamodelImpl.java | 49 ++++++++++++------- .../domain/internal/MappingMetamodelImpl.java | 8 +-- .../internal/BasicDotIdentifierConsumer.java | 1 + .../internal/QualifiedJoinPathConsumer.java | 8 +-- .../hql/internal/SemanticQueryBuilder.java | 9 ++-- .../internal/EmbeddableFetchImpl.java | 8 ++- 7 files changed, 64 insertions(+), 31 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/JpaMetamodel.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/JpaMetamodel.java index ca496516c904..9a9c6e98423a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/JpaMetamodel.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/JpaMetamodel.java @@ -33,7 +33,10 @@ public interface JpaMetamodel extends Metamodel { /** * Access to a managed type through its name + * + * @deprecated This method performs an unchecked type cast */ + @Deprecated(since = "7.2") ManagedDomainType managedType(String typeName); /** @@ -49,13 +52,19 @@ public interface JpaMetamodel extends Metamodel { /** * Specialized handling for resolving entity-name references in * an HQL query + * + * @deprecated This method performs an unchecked type cast */ + @Deprecated(since = "7.2") EntityDomainType getHqlEntityReference(String entityName); /** * Specialized handling for resolving entity-name references in * an HQL query + * + * @deprecated This method performs an unchecked type cast */ + @Deprecated(since = "7.2") EntityDomainType resolveHqlEntityReference(String entityName); /** @@ -79,7 +88,10 @@ public interface JpaMetamodel extends Metamodel { /** * Same as {@link #managedType(String)} except {@code null} is returned rather * than throwing an exception + * + * @deprecated This method performs an unchecked type cast */ + @Deprecated(since = "7.2") @Nullable ManagedDomainType findManagedType(@Nullable String typeName); /** diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java index 410ae933c825..c0694990ba70 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java @@ -14,6 +14,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; @@ -115,7 +116,7 @@ public ServiceRegistry getServiceRegistry() { return serviceRegistry; } - @Override + @Override @Deprecated public @Nullable ManagedDomainType findManagedType(@Nullable String typeName) { //noinspection unchecked return typeName == null ? null : (ManagedDomainType) managedTypeByName.get( typeName ); @@ -192,9 +193,10 @@ public EmbeddableDomainType embeddable(String embeddableName) { return embeddableType; } - @Override + @Override @Deprecated public EntityDomainType getHqlEntityReference(String entityName) { Class loadedClass = null; + //noinspection unchecked final var importInfo = (ImportInfo) resolveImport( entityName ); if ( importInfo != null ) { loadedClass = importInfo.loadedClass; @@ -208,6 +210,7 @@ public EntityDomainType getHqlEntityReference(String entityName) { } if ( loadedClass == null ) { + //noinspection unchecked loadedClass = (Class) resolveRequestedClass( entityName ); // populate the class cache for boot metamodel imports if ( importInfo != null && loadedClass != null ) { @@ -220,7 +223,7 @@ public EntityDomainType getHqlEntityReference(String entityName) { return null; } - @Override + @Override @Deprecated public EntityDomainType resolveHqlEntityReference(String entityName) { final EntityDomainType hqlEntityReference = getHqlEntityReference( entityName ); if ( hqlEntityReference == null ) { @@ -229,11 +232,22 @@ public EntityDomainType resolveHqlEntityReference(String entityName) { return hqlEntityReference; } + private static ManagedDomainType checkDomainType(Class cls, ManagedDomainType domainType) { + if ( domainType != null && !Objects.equals( domainType.getJavaType(), cls ) ) { + throw new IllegalStateException( "Managed type " + domainType + + " has a different Java type than requested" ); + } + else { + @SuppressWarnings("unchecked") // Safe, we checked it + final var type = (ManagedDomainType) domainType; + return type; + } + } + @Override @Nullable public ManagedDomainType findManagedType(Class cls) { - //noinspection unchecked - return (ManagedDomainType) managedTypeByClass.get( cls ); + return checkDomainType( cls, managedTypeByClass.get( cls ) ); } @Override @@ -249,10 +263,9 @@ public ManagedDomainType managedType(Class cls) { @Override @Nullable public EntityDomainType findEntityType(Class cls) { - final var type = managedTypeByClass.get( cls ); - //noinspection unchecked - return type instanceof EntityDomainType - ? (EntityDomainType) type + return checkDomainType( cls, managedTypeByClass.get( cls ) ) + instanceof EntityDomainType entityDomainType + ? entityDomainType : null; } @@ -267,10 +280,9 @@ public EntityDomainType entity(Class cls) { @Override public @Nullable EmbeddableDomainType findEmbeddableType(Class cls) { - final var type = managedTypeByClass.get( cls ); - //noinspection unchecked - return type instanceof EmbeddableDomainType - ? (EmbeddableDomainType) type + return checkDomainType( cls, managedTypeByClass.get( cls ) ) + instanceof EmbeddableDomainType embeddableDomainType + ? embeddableDomainType : null; } @@ -740,17 +752,20 @@ private EntityTypeImpl entityType( final var supertype = (IdentifiableDomainType) supertypeForPersistentClass( persistentClass, context, typeConfiguration ); - final JavaType javaType; + return new EntityTypeImpl<>( entityJavaType( mappedClass, context ), + supertype, persistentClass, this ); + } + + private static JavaType entityJavaType(Class mappedClass, MetadataContext context) { if ( mappedClass == null || Map.class.isAssignableFrom( mappedClass ) ) { // dynamic map //noinspection unchecked - javaType = (JavaType) new DynamicModelJavaType(); + return (JavaType) new DynamicModelJavaType(); } else { - javaType = context.getTypeConfiguration().getJavaTypeRegistry() + return context.getTypeConfiguration().getJavaTypeRegistry() .resolveEntityTypeDescriptor( mappedClass ); } - return new EntityTypeImpl<>( javaType, supertype, persistentClass, this ); } private void handleUnusedMappedSuperclasses(MetadataContext context, TypeConfiguration typeConfiguration) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java index 33209e400641..a5c20d424709 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java @@ -418,12 +418,12 @@ public Set> getEmbeddables() { return jpaMetamodel.getEmbeddables(); } - @Override + @Override @Deprecated public @Nullable ManagedDomainType findManagedType(@Nullable String typeName) { return jpaMetamodel.findManagedType( typeName ); } - @Override + @Override @Deprecated public ManagedDomainType managedType(String typeName) { return jpaMetamodel.managedType( typeName ); } @@ -448,12 +448,12 @@ public EmbeddableDomainType embeddable(String embeddableName) { return jpaMetamodel.embeddable( embeddableName ); } - @Override + @Override @Deprecated public EntityDomainType getHqlEntityReference(String entityName) { return jpaMetamodel.getHqlEntityReference( entityName ); } - @Override + @Override @Deprecated public EntityDomainType resolveHqlEntityReference(String entityName) { return jpaMetamodel.resolveHqlEntityReference( entityName ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java index 1cd1d5d0e2ac..0d1ad4d88b6b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java @@ -99,6 +99,7 @@ public void consumeTreat(String importableName, boolean isTerminal) { private Class treatTarget(String typeName) { final ManagedDomainType managedType = creationState.getCreationContext().getJpaMetamodel() + // TODO: don't use this unsafe, deprecated method .managedType( typeName ); return managedType.getJavaType(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java index 7f2e838a6067..5f013adb41dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java @@ -308,9 +308,11 @@ public void consumeTreat(String typeName, boolean isTerminal) { } private Class treatTarget(String typeName) { - final ManagedDomainType managedType = creationState.getCreationContext() - .getJpaMetamodel() - .managedType( typeName ); + final ManagedDomainType managedType = + creationState.getCreationContext() + .getJpaMetamodel() + // TODO: don't use this unsafe, deprecated method + .managedType( typeName ); return managedType.getJavaType(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 2e721a911f52..7dfe199268ee 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -2138,9 +2138,8 @@ public final SqmCrossJoin visitCrossJoin(HqlParser.CrossJoinContext ctx) { protected void consumeCrossJoin(HqlParser.CrossJoinContext parserJoin, SqmRoot sqmRoot) { final String name = getEntityName( parserJoin.entityName() ); -// SqmTreeCreationLogger.LOGGER.tracef( "Handling root path - %s", name ); - - final var entityDescriptor = getJpaMetamodel().resolveHqlEntityReference( name ); + final EntityDomainType entityDescriptor = + getJpaMetamodel().resolveHqlEntityReference( name ); if ( entityDescriptor instanceof SqmPolymorphicRootDescriptor ) { throw new SemanticException( "Unmapped polymorphic reference cannot be used as a target of 'cross join'", @@ -2579,8 +2578,8 @@ else if ( r instanceof AnyDiscriminatorSqmPath anyDiscriminatorPath && l inst ); } - private SqmExpression createDiscriminatorValue( - AnyDiscriminatorSqmPath anyDiscriminatorTypeSqmPath, + private SqmExpression createDiscriminatorValue( + AnyDiscriminatorSqmPath anyDiscriminatorTypeSqmPath, HqlParser.ExpressionContext valueExpressionContext) { final var expressible = anyDiscriminatorTypeSqmPath.getExpressible(); return new SqmAnyDiscriminatorValue<>( diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java index 23445bb15838..116f068bf3dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java @@ -11,6 +11,7 @@ import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.model.domain.JpaMetamodel; +import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.tree.expression.Expression; @@ -195,8 +196,11 @@ public EmbeddableInitializer createInitializer(InitializerParent parent, A @Override public boolean appliesTo(GraphImplementor graphImplementor, JpaMetamodel metamodel) { - // We use managedType here since this fetch could correspond to an entity type if the embeddable is an id-class - return GraphHelper.appliesTo( graphImplementor, metamodel.managedType( getResultJavaType().getTypeName() ) ); + // We use managedType here since this fetch could correspond + // to an entity type if the embeddable is an @IdClass + final ManagedDomainType managedType = + metamodel.managedType( getResultJavaType().getTypeName() ); + return GraphHelper.appliesTo( graphImplementor, managedType ); } @Override From ccf42f06af02a5635cce311c479120c242dd82cf Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 7 Dec 2025 14:53:49 +0100 Subject: [PATCH 046/181] actually fix up the unsafe methods of JpaMetamodel this change exposed multiple bugs! 1. generic attributes declared by a mapped superclass where not being handled properly in HQL path expressions 2. getSubTypes() on EntityTypeImpl was broken (due, again, to use of unchecked cast!) causing problems in HQL --- .../InFlightMetadataCollectorImpl.java | 2 +- .../model/internal/ClassPropertyHolder.java | 35 ++++++------- .../metamodel/internal/AttributeFactory.java | 2 +- .../metamodel/model/domain/JpaMetamodel.java | 20 ++------ .../internal/AbstractIdentifiableType.java | 38 +++++++------- .../domain/internal/DomainModelHelper.java | 13 +++-- .../domain/internal/EmbeddableTypeImpl.java | 22 ++++++-- .../model/domain/internal/EntityTypeImpl.java | 37 ++++++++++---- .../domain/internal/JpaMetamodelImpl.java | 50 +++++++++---------- .../internal/MappedSuperclassTypeImpl.java | 2 +- .../domain/internal/MappingMetamodelImpl.java | 20 ++++---- .../internal/BasicDotIdentifierConsumer.java | 20 ++++---- .../internal/QualifiedJoinPathConsumer.java | 50 ++++++++++++------- .../hql/internal/SemanticQueryBuilder.java | 3 +- .../hibernate/query/sqm/SqmPathSource.java | 2 +- .../query/sqm/internal/TypecheckUtil.java | 2 +- .../internal/EmbeddableFetchImpl.java | 6 +-- .../validation/MockSessionFactory.java | 4 +- 18 files changed, 177 insertions(+), 151 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java index 186fbb1da563..052109c1767d 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java @@ -1767,7 +1767,7 @@ private void processValueResolvers(MetadataBuildingContext buildingContext) { private void processSecondPasses(ArrayList secondPasses) { if ( secondPasses != null ) { - for ( SecondPass secondPass : secondPasses ) { + for ( var secondPass : secondPasses ) { secondPass.doSecondPass( getEntityBindingMap() ); } secondPasses.clear(); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ClassPropertyHolder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ClassPropertyHolder.java index 2ec9461d3656..413c478f4d56 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ClassPropertyHolder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ClassPropertyHolder.java @@ -12,7 +12,6 @@ import org.hibernate.AssertionFailure; import org.hibernate.PropertyNotFoundException; import org.hibernate.boot.spi.MetadataBuildingContext; -import org.hibernate.boot.spi.SecondPass; import org.hibernate.mapping.Collection; import org.hibernate.mapping.Component; import org.hibernate.mapping.IndexedCollection; @@ -312,29 +311,25 @@ static void prepareActualProperty( if ( !allowCollections ) { throw new AssertionFailure( "Collections are not allowed as identifier properties" ); } - // The owner is a MappedSuperclass which is not a PersistentClass, so set it to null -// collection.setOwner( null ); + // The owner is a MappedSuperclass, not a PersistentClass, + // so set it to null collection.setOwner( null ); collection.setRole( memberDetails.getDeclaringType().getName() + "." + property.getName() ); // To copy the element and key values, we need to defer setting the type name until the CollectionBinder ran final var originalValue = property.getValue(); - context.getMetadataCollector().addSecondPass( - new SecondPass() { - @Override - public void doSecondPass(Map persistentClasses) { - final var initializedCollection = (Collection) originalValue; - final var element = initializedCollection.getElement().copy(); - setTypeName( element, memberDetails.getElementType().getName() ); - if ( initializedCollection instanceof IndexedCollection indexedCollection ) { - final var index = indexedCollection.getIndex().copy(); - if ( memberDetails.getMapKeyType() != null ) { - setTypeName( index, memberDetails.getMapKeyType().getName() ); - } - ( (IndexedCollection) collection ).setIndex( index ); - } - collection.setElement( element ); - } + context.getMetadataCollector().addSecondPass( persistentClasses -> { + final var initializedCollection = (Collection) originalValue; + final var element = initializedCollection.getElement().copy(); + setTypeName( element, memberDetails.getElementType().getName() ); + if ( initializedCollection instanceof IndexedCollection indexedCollection ) { + final var index = indexedCollection.getIndex().copy(); + final var mapKeyType = memberDetails.getMapKeyType(); + if ( mapKeyType != null ) { + setTypeName( index, mapKeyType.getName() ); } - ); + ( (IndexedCollection) collection ).setIndex( index ); + } + collection.setElement( element ); + } ); } else { setTypeName( value, memberDetails.getType().getName() ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AttributeFactory.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AttributeFactory.java index 9277ea7e1098..8c43a2cebed6 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AttributeFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AttributeFactory.java @@ -212,7 +212,7 @@ private DomainType determineSimpleType(ValueContext typeContext) { public static DomainType determineSimpleType(ValueContext typeContext, MetadataContext context) { return switch ( typeContext.getValueClassification() ) { case BASIC -> basicDomainType( typeContext, context ); - case ENTITY -> entityDomainType (typeContext, context ); + case ENTITY -> entityDomainType( typeContext, context ); case EMBEDDABLE -> embeddableDomainType( typeContext, context ); default -> throw new AssertionFailure( "Unknown type : " + typeContext.getValueClassification() ); }; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/JpaMetamodel.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/JpaMetamodel.java index 9a9c6e98423a..2d0b9166c5f8 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/JpaMetamodel.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/JpaMetamodel.java @@ -33,11 +33,8 @@ public interface JpaMetamodel extends Metamodel { /** * Access to a managed type through its name - * - * @deprecated This method performs an unchecked type cast */ - @Deprecated(since = "7.2") - ManagedDomainType managedType(String typeName); + ManagedDomainType managedType(String typeName); /** * Access to an entity supporting Hibernate's entity-name feature @@ -52,20 +49,14 @@ public interface JpaMetamodel extends Metamodel { /** * Specialized handling for resolving entity-name references in * an HQL query - * - * @deprecated This method performs an unchecked type cast */ - @Deprecated(since = "7.2") - EntityDomainType getHqlEntityReference(String entityName); + EntityDomainType getHqlEntityReference(String entityName); /** * Specialized handling for resolving entity-name references in * an HQL query - * - * @deprecated This method performs an unchecked type cast */ - @Deprecated(since = "7.2") - EntityDomainType resolveHqlEntityReference(String entityName); + EntityDomainType resolveHqlEntityReference(String entityName); /** * Same as {@link #managedType(Class)} except {@code null} is returned rather @@ -88,11 +79,8 @@ public interface JpaMetamodel extends Metamodel { /** * Same as {@link #managedType(String)} except {@code null} is returned rather * than throwing an exception - * - * @deprecated This method performs an unchecked type cast */ - @Deprecated(since = "7.2") - @Nullable ManagedDomainType findManagedType(@Nullable String typeName); + @Nullable ManagedDomainType findManagedType(@Nullable String typeName); /** * Same as {@link #entity(String)} except {@code null} is returned rather diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractIdentifiableType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractIdentifiableType.java index e3edd41e03c2..6b423d5c2d04 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractIdentifiableType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractIdentifiableType.java @@ -114,14 +114,15 @@ public boolean hasSingleIdAttribute() { } @Override - @SuppressWarnings("unchecked") public SqmSingularPersistentAttribute getId(Class javaType) { ensureNoIdClass(); final var id = findIdAttribute(); if ( id != null ) { checkType( id, javaType ); } - return (SqmSingularPersistentAttribute) id; + @SuppressWarnings("unchecked") // safe, we just checked + final var castId = (SqmSingularPersistentAttribute) id; + return castId; } private void ensureNoIdClass() { @@ -148,8 +149,7 @@ else if ( getSuperType() != null ) { private void checkType(SingularPersistentAttribute attribute, Class javaType) { if ( !javaType.isAssignableFrom( attribute.getType().getJavaType() ) ) { - final JavaType attributeJavaType = attribute.getAttributeJavaType(); - if ( !( attributeJavaType instanceof PrimitiveJavaType primitiveJavaType ) + if ( !( attribute.getAttributeJavaType() instanceof PrimitiveJavaType primitiveJavaType ) || primitiveJavaType.getPrimitiveClass() != javaType ) { throw new IllegalArgumentException( String.format( @@ -165,14 +165,15 @@ private void checkType(SingularPersistentAttribute attribute, Class jav } @Override - @SuppressWarnings("unchecked") public SqmSingularPersistentAttribute getDeclaredId(Class javaType) { ensureNoIdClass(); if ( id == null ) { throw new IllegalArgumentException( "The id attribute is not declared on this type [" + getTypeName() + "]" ); } checkType( id, javaType ); - return (SqmSingularPersistentAttribute) id; + @SuppressWarnings("unchecked") // safe, we just checked + final var castId = (SqmSingularPersistentAttribute) id; + return castId; } @Override @@ -226,14 +227,16 @@ else if ( idClassType instanceof SimpleDomainType simpleDomainType ) { } @Override - @SuppressWarnings("unchecked") public void visitIdClassAttributes(Consumer> attributeConsumer) { if ( nonAggregatedIdAttributes != null ) { nonAggregatedIdAttributes.forEach( attributeConsumer ); } - else if ( getSuperType() != null ) { - //noinspection rawtypes - getSuperType().visitIdClassAttributes( (Consumer) attributeConsumer ); + else { + final var superType = getSuperType(); + if ( superType != null ) { + //noinspection rawtypes, unchecked + superType.visitIdClassAttributes( (Consumer) attributeConsumer ); + } } } @@ -247,14 +250,15 @@ public boolean hasDeclaredVersionAttribute() { } @Override - @SuppressWarnings("unchecked") public SingularPersistentAttribute getVersion(Class javaType) { if ( hasVersionAttribute() ) { final var version = findVersionAttribute(); if ( version != null ) { checkType( version, javaType ); } - return (SingularPersistentAttribute) version; + @SuppressWarnings("unchecked") // safe, we just checked + final var castVersion = (SingularPersistentAttribute) version; + return castVersion; } else { return null; @@ -288,11 +292,12 @@ else if ( getSuperType() != null ) { } @Override - @SuppressWarnings("unchecked") public SingularPersistentAttribute getDeclaredVersion(Class javaType) { checkDeclaredVersion(); checkType( versionAttribute, javaType ); - return (SingularPersistentAttribute) versionAttribute; + @SuppressWarnings("unchecked") // safe, we just checked + final var castVersion = (SingularPersistentAttribute) versionAttribute; + return castVersion; } private void checkDeclaredVersion() { @@ -360,9 +365,8 @@ public void applyNonAggregatedIdAttributes( nonAggregatedIdAttributes.add( (SqmSingularPersistentAttribute) idAttribute ); if ( AbstractIdentifiableType.this == idAttribute.getDeclaringType() ) { @SuppressWarnings("unchecked") - // Safe, because we know it's declared by this type - final PersistentAttribute declaredAttribute = - (PersistentAttribute) idAttribute; + // Safe, because we know it's declared by this type + final var declaredAttribute = (PersistentAttribute) idAttribute; addAttribute( declaredAttribute ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/DomainModelHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/DomainModelHelper.java index d01d2ef09ac3..f4cfb2433533 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/DomainModelHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/DomainModelHelper.java @@ -5,7 +5,6 @@ package org.hibernate.metamodel.model.domain.internal; import org.hibernate.metamodel.MappingMetamodel; -import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.ManagedDomainType; @@ -28,9 +27,9 @@ static boolean isCompatible( return true; } else { - final ModelPart modelPart1 = + final var modelPart1 = getEntityAttributeModelPart( attribute1, attribute1.getDeclaringType(), mappingMetamodel ); - final ModelPart modelPart2 = + final var modelPart2 = getEntityAttributeModelPart( attribute2, attribute2.getDeclaringType(), mappingMetamodel ); return modelPart1 != null && modelPart2 != null @@ -43,13 +42,13 @@ static ModelPart getEntityAttributeModelPart( ManagedDomainType domainType, MappingMetamodel mappingMetamodel) { if ( domainType instanceof EntityDomainType ) { - final EntityMappingType entity = mappingMetamodel.getEntityDescriptor( domainType.getTypeName() ); - return entity.findSubPart( attribute.getName() ); + return mappingMetamodel.getEntityDescriptor( domainType.getTypeName() ) + .findSubPart( attribute.getName() ); } else { ModelPart candidate = null; - for ( ManagedDomainType subType : domainType.getSubTypes() ) { - final ModelPart modelPart = getEntityAttributeModelPart( attribute, subType, mappingMetamodel ); + for ( var subType : domainType.getSubTypes() ) { + final var modelPart = getEntityAttributeModelPart( attribute, subType, mappingMetamodel ); if ( modelPart != null ) { if ( candidate != null && !isCompatibleModelPart( candidate, modelPart ) ) { return null; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddableTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddableTypeImpl.java index 267c7cba8051..223e6ee38cc1 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddableTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddableTypeImpl.java @@ -5,9 +5,12 @@ package org.hibernate.metamodel.model.domain.internal; import java.io.Serializable; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.AssertionFailure; import org.hibernate.metamodel.UnsupportedMappingException; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.model.domain.DomainType; @@ -33,6 +36,7 @@ public class EmbeddableTypeImpl implements SqmEmbeddableDomainType, Serializable { private final boolean isDynamic; private final EmbeddedDiscriminatorSqmPathSource discriminatorPathSource; + private final List> subtypes = new ArrayList<>(); public EmbeddableTypeImpl( JavaType javaType, @@ -61,15 +65,27 @@ public PersistenceType getPersistenceType() { public int getTupleLength() { int count = 0; for ( var attribute : getSingularAttributes() ) { - count += ( (SqmDomainType) attribute.getType() ).getTupleLength(); + if ( attribute.getType() instanceof SqmDomainType domainType ) { + count += domainType.getTupleLength(); + } + else { + throw new AssertionFailure( "Should have been a domain type" ); + } } return count; } @Override public Collection> getSubTypes() { - //noinspection unchecked - return (Collection>) super.getSubTypes(); + return subtypes; + } + + @Override + public void addSubType(ManagedDomainType subType) { + super.addSubType( subType ); + if ( subType instanceof SqmEmbeddableDomainType entityDomainType ) { + subtypes.add( entityDomainType ); + } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityTypeImpl.java index b9f2da1a067a..0fa0b6261c57 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityTypeImpl.java @@ -7,7 +7,9 @@ import java.io.ObjectStreamException; import java.io.Serial; import java.io.Serializable; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Locale; import jakarta.persistence.metamodel.EntityType; @@ -15,19 +17,17 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.mapping.PersistentClass; import org.hibernate.metamodel.UnsupportedMappingException; -import org.hibernate.metamodel.mapping.DiscriminatorType; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.model.domain.IdentifiableDomainType; import org.hibernate.metamodel.model.domain.JpaMetamodel; +import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.metamodel.model.domain.PersistentAttribute; import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor; -import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.PathException; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.tree.domain.SqmDomainType; -import org.hibernate.query.sqm.tree.domain.SqmManagedDomainType; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPersistentAttribute; import org.hibernate.query.sqm.tree.domain.SqmEntityDomainType; @@ -35,6 +35,7 @@ import static jakarta.persistence.metamodel.Bindable.BindableType.ENTITY_TYPE; import static jakarta.persistence.metamodel.Type.PersistenceType.ENTITY; +import static jakarta.persistence.metamodel.Type.PersistenceType.MAPPED_SUPERCLASS; import static org.hibernate.metamodel.model.domain.internal.DomainModelHelper.isCompatible; /** @@ -50,6 +51,7 @@ public class EntityTypeImpl private final String jpaEntityName; private final JpaMetamodelImplementor metamodel; private final SqmPathSource discriminatorPathSource; + private final List> subtypes = new ArrayList<>(); public EntityTypeImpl( String entityName, @@ -77,10 +79,10 @@ public EntityTypeImpl( } private EntityDiscriminatorSqmPathSource entityDiscriminatorPathSource(JpaMetamodelImplementor metamodel) { - final EntityPersister entityDescriptor = + final var entityDescriptor = metamodel.getMappingMetamodel() .getEntityDescriptor( getHibernateEntityName() ); - final DiscriminatorType discriminatorType = entityDescriptor.getDiscriminatorDomainType(); + final var discriminatorType = entityDescriptor.getDiscriminatorDomainType(); return discriminatorType == null ? null : new EntityDiscriminatorSqmPathSource<>( discriminatorType, this, entityDescriptor ); } @@ -176,13 +178,19 @@ else if ( EntityDiscriminatorMapping.matchesRoleName( name ) ) { @Override public @Nullable SqmPathSource findSubPathSource(String name, boolean includeSubtypes) { - final PersistentAttribute attribute = super.findAttribute( name ); + final var attribute = super.findAttribute( name ); if ( attribute != null ) { + if ( attribute.getDeclaringType().getPersistenceType() == MAPPED_SUPERCLASS ) { + final var concreteGeneric = findConcreteGenericAttribute( name ); + if ( concreteGeneric != null ) { + return (SqmPathSource) concreteGeneric; + } + } return (SqmPathSource) attribute; } else { if ( includeSubtypes ) { - final PersistentAttribute subtypeAttribute = findSubtypeAttribute( name ); + final var subtypeAttribute = findSubtypeAttribute( name ); if ( subtypeAttribute != null ) { return (SqmPathSource) subtypeAttribute; } @@ -201,8 +209,8 @@ else if ( EntityDiscriminatorMapping.matchesRoleName( name ) ) { private SqmPersistentAttribute findSubtypeAttribute(String name) { SqmPersistentAttribute subtypeAttribute = null; - for ( SqmManagedDomainType subtype : getSubTypes() ) { - final SqmPersistentAttribute candidate = subtype.findSubTypesAttribute( name ); + for ( var subtype : super.getSubTypes() ) { + final var candidate = subtype.findSubTypesAttribute( name ); if ( candidate != null ) { if ( subtypeAttribute != null && !isCompatible( subtypeAttribute, candidate, metamodel.getMappingMetamodel() ) ) { @@ -249,8 +257,15 @@ public PersistenceType getPersistenceType() { @Override public Collection> getSubTypes() { - //noinspection unchecked - return (Collection>) super.getSubTypes(); + return subtypes; + } + + @Override + public void addSubType(ManagedDomainType subType) { + super.addSubType( subType ); + if ( subType instanceof SqmEntityDomainType entityDomainType ) { + subtypes.add( entityDomainType ); + } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java index c0694990ba70..f0ffc1f23837 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java @@ -67,11 +67,11 @@ */ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable { - private static class ImportInfo { + private static class ImportInfo { private final String importedName; - private Class loadedClass; // could be null for boot metamodel import; not final to allow for populating later + private Class loadedClass; // could be null for boot metamodel import; not final to allow for populating later - private ImportInfo(String importedName, Class loadedClass) { + private ImportInfo(String importedName, Class loadedClass) { this.importedName = importedName; this.loadedClass = loadedClass; } @@ -94,7 +94,7 @@ private ImportInfo(String importedName, Class loadedClass) { private final Map, String> entityProxyInterfaceMap = new HashMap<>(); - private final Map> nameToImportMap = new ConcurrentHashMap<>(); + private final Map nameToImportMap = new ConcurrentHashMap<>(); private final Map knownInvalidnameToImportMap = new ConcurrentHashMap<>(); @@ -116,15 +116,14 @@ public ServiceRegistry getServiceRegistry() { return serviceRegistry; } - @Override @Deprecated - public @Nullable ManagedDomainType findManagedType(@Nullable String typeName) { - //noinspection unchecked - return typeName == null ? null : (ManagedDomainType) managedTypeByName.get( typeName ); + @Override + public @Nullable ManagedDomainType findManagedType(@Nullable String typeName) { + return typeName == null ? null : managedTypeByName.get( typeName ); } @Override - public ManagedDomainType managedType(String typeName) { - final ManagedDomainType managedType = findManagedType( typeName ); + public ManagedDomainType managedType(String typeName) { + final var managedType = findManagedType( typeName ); if ( managedType == null ) { throw new IllegalArgumentException( "Not a managed type: " + typeName ); } @@ -193,11 +192,10 @@ public EmbeddableDomainType embeddable(String embeddableName) { return embeddableType; } - @Override @Deprecated - public EntityDomainType getHqlEntityReference(String entityName) { - Class loadedClass = null; - //noinspection unchecked - final var importInfo = (ImportInfo) resolveImport( entityName ); + @Override + public EntityDomainType getHqlEntityReference(String entityName) { + Class loadedClass = null; + final var importInfo = resolveImport( entityName ); if ( importInfo != null ) { loadedClass = importInfo.loadedClass; entityName = importInfo.importedName; @@ -205,13 +203,11 @@ public EntityDomainType getHqlEntityReference(String entityName) { final var entityDescriptor = findEntityType( entityName ); if ( entityDescriptor != null ) { - //noinspection unchecked - return (EntityDomainType) entityDescriptor; + return entityDescriptor; } if ( loadedClass == null ) { - //noinspection unchecked - loadedClass = (Class) resolveRequestedClass( entityName ); + loadedClass = resolveRequestedClass( entityName ); // populate the class cache for boot metamodel imports if ( importInfo != null && loadedClass != null ) { importInfo.loadedClass = loadedClass; @@ -223,9 +219,9 @@ public EntityDomainType getHqlEntityReference(String entityName) { return null; } - @Override @Deprecated - public EntityDomainType resolveHqlEntityReference(String entityName) { - final EntityDomainType hqlEntityReference = getHqlEntityReference( entityName ); + @Override + public EntityDomainType resolveHqlEntityReference(String entityName) { + final var hqlEntityReference = getHqlEntityReference( entityName ); if ( hqlEntityReference == null ) { throw new EntityTypeException( "Could not resolve entity name '" + entityName + "'", entityName ); } @@ -444,11 +440,11 @@ public Map> getNamedEntityGraphs(Class e @Override public String qualifyImportableName(String queryName) { - final ImportInfo importInfo = resolveImport( queryName ); + final var importInfo = resolveImport( queryName ); return importInfo == null ? null : importInfo.importedName; } - private ImportInfo resolveImport(final String name) { + private ImportInfo resolveImport(final String name) { final var importInfo = nameToImportMap.get( name ); //optimal path first if ( importInfo != null ) { @@ -461,7 +457,7 @@ private ImportInfo resolveImport(final String name) { } else { // see if the name is a fully qualified class name - final Class loadedClass = resolveRequestedClass( name ); + final var loadedClass = resolveRequestedClass( name ); if ( loadedClass == null ) { // it is NOT a fully qualified class name - add a marker entry, so we do not keep trying later // note that ConcurrentHashMap does not support null value so a marker entry is needed @@ -481,7 +477,7 @@ private ImportInfo resolveImport(final String name) { else { // it is a fully qualified class name - add it to the cache // so to not needing to load from the classloader again - final ImportInfo info = new ImportInfo<>( name, loadedClass ); + final var info = new ImportInfo( name, loadedClass ); nameToImportMap.put( name, info ); return info; } @@ -609,7 +605,7 @@ public void processJpa( RuntimeModelCreationContext runtimeModelCreationContext) { bootMetamodel.getImports() .forEach( (key, value) -> nameToImportMap.put( key, - new ImportInfo<>( value, null ) ) ); + new ImportInfo( value, null ) ) ); this.entityProxyInterfaceMap.putAll( entityProxyInterfaceMap ); final var context = new MetadataContext( diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java index eb6030e662f8..13549780a83f 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java @@ -103,7 +103,7 @@ else if ( "id".equalsIgnoreCase( name ) ) { @Override public @Nullable SqmPathSource getIdentifierDescriptor() { - return (SqmPathSource) super.getIdentifierDescriptor(); + return super.getIdentifierDescriptor(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java index a5c20d424709..30887186f042 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java @@ -418,13 +418,13 @@ public Set> getEmbeddables() { return jpaMetamodel.getEmbeddables(); } - @Override @Deprecated - public @Nullable ManagedDomainType findManagedType(@Nullable String typeName) { + @Override + public @Nullable ManagedDomainType findManagedType(@Nullable String typeName) { return jpaMetamodel.findManagedType( typeName ); } - @Override @Deprecated - public ManagedDomainType managedType(String typeName) { + @Override + public ManagedDomainType managedType(String typeName) { return jpaMetamodel.managedType( typeName ); } @@ -448,13 +448,13 @@ public EmbeddableDomainType embeddable(String embeddableName) { return jpaMetamodel.embeddable( embeddableName ); } - @Override @Deprecated - public EntityDomainType getHqlEntityReference(String entityName) { + @Override + public EntityDomainType getHqlEntityReference(String entityName) { return jpaMetamodel.getHqlEntityReference( entityName ); } - @Override @Deprecated - public EntityDomainType resolveHqlEntityReference(String entityName) { + @Override + public EntityDomainType resolveHqlEntityReference(String entityName) { return jpaMetamodel.resolveHqlEntityReference( entityName ); } @@ -650,13 +650,13 @@ public String[] getAllCollectionRoles() { return basicType; } - final ManagedDomainType managedType = jpaMetamodel.findManagedType( javaType ); + final var managedType = jpaMetamodel.findManagedType( javaType ); if ( managedType != null ) { return (BindableType) managedType; } final var javaTypeRegistry = typeConfiguration.getJavaTypeRegistry(); - final JavaType javaTypeDescriptor = javaTypeRegistry.findDescriptor( javaType ); + final var javaTypeDescriptor = javaTypeRegistry.findDescriptor( javaType ); if ( javaTypeDescriptor != null ) { final JdbcType recommendedJdbcType = javaTypeDescriptor.getRecommendedJdbcType( typeConfiguration.getCurrentBaseSqlTypeIndicators() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java index 0d1ad4d88b6b..265c9ea46e8e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java @@ -5,7 +5,6 @@ package org.hibernate.query.hql.internal; import org.hibernate.metamodel.model.domain.JpaMetamodel; -import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.query.SemanticException; import org.hibernate.query.hql.spi.DotIdentifierConsumer; import org.hibernate.query.hql.spi.SemanticPathPart; @@ -16,6 +15,7 @@ import org.hibernate.query.sqm.function.SqmFunctionDescriptor; import org.hibernate.query.sqm.spi.SqmCreationContext; import org.hibernate.query.sqm.tree.domain.SqmPath; +import org.hibernate.query.sqm.tree.domain.SqmTreatedPath; import org.hibernate.query.sqm.tree.expression.SqmEnumLiteral; import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.query.sqm.tree.expression.SqmFieldLiteral; @@ -92,16 +92,18 @@ public void consumeIdentifier(String identifier, boolean isBase, boolean isTermi @Override public void consumeTreat(String importableName, boolean isTerminal) { - final SqmPath sqmPath = (SqmPath) currentPart; - currentPart = sqmPath.treatAs( treatTarget( importableName ) ); + currentPart = treat( importableName, (SqmPath) currentPart ); } - private Class treatTarget(String typeName) { - final ManagedDomainType managedType = + private SqmTreatedPath treat(String importableName, SqmPath path) { + return path.treatAs( treatTarget( path, importableName ) ); + } + + private Class treatTarget(SqmPath path, String typeName) { + final var javaType = creationState.getCreationContext().getJpaMetamodel() - // TODO: don't use this unsafe, deprecated method - .managedType( typeName ); - return managedType.getJavaType(); + .managedType( typeName ).getJavaType(); + return javaType.asSubclass( path.getJavaType() ); } protected void reset() { @@ -214,7 +216,7 @@ private SemanticPathPart resolveLiteralType(JpaMetamodel jpaMetamodel, NodeBuild return null; } else { - final ManagedDomainType managedType = jpaMetamodel.managedType( importableName ); + final var managedType = jpaMetamodel.managedType( importableName ); if ( managedType instanceof SqmEntityDomainType entityDomainType ) { return new SqmLiteralEntityType<>( entityDomainType, nodeBuilder ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java index 5f013adb41dc..280f48f4445a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java @@ -4,8 +4,6 @@ */ package org.hibernate.query.hql.internal; -import org.hibernate.metamodel.model.domain.EntityDomainType; -import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.query.PathException; import org.hibernate.query.SemanticException; import org.hibernate.query.hql.spi.DotIdentifierConsumer; @@ -17,9 +15,9 @@ import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.spi.SqmCreationHelper; import org.hibernate.query.sqm.tree.SqmJoinType; -import org.hibernate.query.sqm.tree.cte.SqmCteStatement; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor; +import org.hibernate.query.sqm.tree.domain.SqmTreatedFrom; import org.hibernate.query.sqm.tree.from.SqmAttributeJoin; import org.hibernate.query.sqm.tree.from.SqmCteJoin; import org.hibernate.query.sqm.tree.from.SqmEntityJoin; @@ -27,6 +25,7 @@ import org.hibernate.query.sqm.tree.from.SqmJoin; import org.hibernate.query.sqm.tree.from.SqmRoot; +import org.hibernate.query.sqm.tree.from.SqmTreatedAttributeJoin; import org.jboss.logging.Logger; import static org.hibernate.query.sqm.internal.SqmUtil.findCompatibleFetchJoin; @@ -240,7 +239,7 @@ else if ( fetch ) { boolean allowReuse, SqmCreationState creationState, SqmJoinable joinSource) { - final SqmJoin join = joinSource.createSqmJoin( + final var join = joinSource.createSqmJoin( lhs, joinType, isTerminal ? alias : allowReuse ? SqmCreationHelper.IMPLICIT_ALIAS : null, @@ -296,24 +295,39 @@ public void consumeIdentifier(String identifier, boolean isTerminal, boolean all @Override public void consumeTreat(String typeName, boolean isTerminal) { + currentPath = treat( typeName, isTerminal ); + creationState.getCurrentProcessingState().getPathRegistry().register( currentPath ); + } + + private SqmTreatedFrom treat(String typeName, boolean isTerminal) { if ( isTerminal ) { - currentPath = fetch - ? ( (SqmAttributeJoin) currentPath ).treatAs( treatTarget( typeName ), alias, true ) - : currentPath.treatAs( treatTarget( typeName ), alias ); + return fetch + ? treatTerminalFetch( currentPath, typeName ) + : treatTerminal( currentPath, typeName ); } else { - currentPath = currentPath.treatAs( treatTarget( typeName ) ); + return treatNonTerminal( currentPath, typeName ); } - creationState.getCurrentProcessingState().getPathRegistry().register( currentPath ); } - private Class treatTarget(String typeName) { - final ManagedDomainType managedType = - creationState.getCreationContext() - .getJpaMetamodel() - // TODO: don't use this unsafe, deprecated method - .managedType( typeName ); - return managedType.getJavaType(); + private SqmTreatedFrom treatNonTerminal(SqmFrom path, String typeName) { + return path.treatAs( treatTarget( path, typeName ) ); + } + + private SqmTreatedAttributeJoin treatTerminalFetch(SqmFrom path, String typeName) { + final var attributeJoin = (SqmAttributeJoin) path; + return attributeJoin.treatAs( treatTarget( path, typeName ), alias, true ); + } + + private SqmTreatedFrom treatTerminal(SqmFrom path, String typeName) { + return path.treatAs( treatTarget( path, typeName ), alias ); + } + + private Class treatTarget(SqmPath path, String typeName) { + final var javaType = + creationState.getCreationContext().getJpaMetamodel() + .managedType( typeName ).getJavaType(); + return javaType.asSubclass( path.getJavaType() ); } @Override @@ -359,11 +373,11 @@ public void consumeIdentifier(String identifier, boolean isTerminal, boolean all path.append( identifier ); if ( isTerminal ) { final String fullPath = path.toString(); - final EntityDomainType joinedEntityType = + final var joinedEntityType = creationState.getCreationContext().getJpaMetamodel() .getHqlEntityReference( fullPath ); if ( joinedEntityType == null ) { - final SqmCteStatement cteStatement = creationState.findCteStatement( fullPath ); + final var cteStatement = creationState.findCteStatement( fullPath ); if ( cteStatement != null ) { //noinspection rawtypes,unchecked join = new SqmCteJoin( cteStatement, alias, joinType, sqmRoot ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 7dfe199268ee..33044d94cabd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -2138,8 +2138,7 @@ public final SqmCrossJoin visitCrossJoin(HqlParser.CrossJoinContext ctx) { protected void consumeCrossJoin(HqlParser.CrossJoinContext parserJoin, SqmRoot sqmRoot) { final String name = getEntityName( parserJoin.entityName() ); - final EntityDomainType entityDescriptor = - getJpaMetamodel().resolveHqlEntityReference( name ); + final var entityDescriptor = getJpaMetamodel().resolveHqlEntityReference( name ); if ( entityDescriptor instanceof SqmPolymorphicRootDescriptor ) { throw new SemanticException( "Unmapped polymorphic reference cannot be used as a target of 'cross join'", diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java index b6634190173f..8416108acfa8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java @@ -88,7 +88,7 @@ default SqmPathSource getSubPathSource(String name) { * @throws IllegalArgumentException if the subPathSource is not found */ default SqmPathSource getSubPathSource(String name, boolean subtypes) { - final SqmPathSource subPathSource = findSubPathSource( name, subtypes ); + final var subPathSource = findSubPathSource( name, subtypes ); if ( subPathSource == null ) { throw new PathElementException( String.format( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java index 46c3af8fb73f..0bcb51539151 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java @@ -402,7 +402,7 @@ private static boolean isMappedSuperclassTypeAssignable( EntityType rhsType, BindingContext bindingContext) { - for ( ManagedDomainType candidate : lhsType.getSubTypes() ) { + for ( var candidate : lhsType.getSubTypes() ) { if ( candidate instanceof EntityType candidateEntityType && isEntityTypeAssignable( candidateEntityType, rhsType, bindingContext ) ) { return true; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java index 116f068bf3dc..7328d783ee8e 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java @@ -11,7 +11,6 @@ import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.model.domain.JpaMetamodel; -import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.tree.expression.Expression; @@ -198,9 +197,8 @@ public EmbeddableInitializer createInitializer(InitializerParent parent, A public boolean appliesTo(GraphImplementor graphImplementor, JpaMetamodel metamodel) { // We use managedType here since this fetch could correspond // to an entity type if the embeddable is an @IdClass - final ManagedDomainType managedType = - metamodel.managedType( getResultJavaType().getTypeName() ); - return GraphHelper.appliesTo( graphImplementor, managedType ); + return GraphHelper.appliesTo( graphImplementor, + metamodel.managedType( getResultJavaType().getTypeName() ) ); } @Override diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockSessionFactory.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockSessionFactory.java index caf404ff29d3..8325307d04fe 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockSessionFactory.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockSessionFactory.java @@ -860,14 +860,14 @@ else if (isEntityDefined(queryName)) { } @Override - public @Nullable ManagedDomainType findManagedType(@Nullable String typeName) { + public @Nullable ManagedDomainType findManagedType(@Nullable String typeName) { // TODO: not every ManagedDomainType is an EntityDomainType! return typeName == null ? null : new MockEntityDomainType<>(new MockJavaType<>(typeName)); } @Override public ManagedDomainType findManagedType(Class cls) { - return findManagedType( cls.getName() ); + return cls == null ? null : new MockEntityDomainType<>(new MockJavaType<>(cls.getName())); } @Override From e491d333ad3db6f4536ebc55d927504430cee9ca Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 7 Dec 2025 17:13:45 +0100 Subject: [PATCH 047/181] fix an accidental change that broke Reactive --- .../results/graph/entity/internal/EntityInitializerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java index 60a1f9c825f1..068253ec19a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java @@ -2112,7 +2112,7 @@ public boolean isHasLazyInitializingSubAssemblers() { return assemblers; } - protected @Nullable BasicResultAssembler discriminatorAssembler() { + protected @Nullable BasicResultAssembler getDiscriminatorAssembler() { return discriminatorAssembler; } From dadc5ed391d5c5758680a5734af59aeae9adcdb5 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 7 Dec 2025 18:57:49 +0100 Subject: [PATCH 048/181] type inference in the SQM tree fixed a method which depended on an unchecked cast --- .../internal/AnyDiscriminatorSqmPath.java | 2 +- .../EmbeddedDiscriminatorSqmPath.java | 2 +- .../internal/EntityDiscriminatorSqmPath.java | 2 +- .../internal/BasicDotIdentifierConsumer.java | 3 +- .../internal/QualifiedJoinPathConsumer.java | 3 +- .../query/hql/internal/QuerySplitter.java | 2 +- .../SelfRenderingSqmAggregateFunction.java | 2 +- .../function/SelfRenderingSqmFunction.java | 2 +- ...nderingSqmOrderedSetAggregateFunction.java | 2 +- .../SelfRenderingSqmSetReturningFunction.java | 2 +- .../SelfRenderingSqmWindowFunction.java | 2 +- .../sqm/internal/SqmCriteriaNodeBuilder.java | 413 +++++++++--------- .../sqm/sql/BaseSqmToSqlAstConverter.java | 2 +- .../sqm/tree/AbstractSqmDmlStatement.java | 26 +- .../AbstractSqmRestrictedDmlStatement.java | 8 +- .../query/sqm/tree/AbstractSqmStatement.java | 2 +- .../query/sqm/tree/cte/SqmCteStatement.java | 2 +- .../sqm/tree/delete/SqmDeleteStatement.java | 2 +- .../sqm/tree/domain/AbstractSqmFrom.java | 63 +-- .../sqm/tree/domain/AbstractSqmJoin.java | 14 +- .../sqm/tree/domain/AbstractSqmPath.java | 148 +++---- .../tree/domain/AbstractSqmSimplePath.java | 2 +- .../NonAggregatedCompositeSimplePath.java | 6 +- .../tree/domain/SqmAnyValuedSimplePath.java | 8 +- .../query/sqm/tree/domain/SqmBagJoin.java | 6 +- .../tree/domain/SqmBasicValuedSimplePath.java | 9 +- .../sqm/tree/domain/SqmCorrelatedBagJoin.java | 4 +- .../tree/domain/SqmCorrelatedCrossJoin.java | 7 +- .../sqm/tree/domain/SqmCorrelatedCteJoin.java | 4 +- .../tree/domain/SqmCorrelatedDerivedJoin.java | 4 +- .../tree/domain/SqmCorrelatedDerivedRoot.java | 4 +- .../domain/SqmCorrelatedDerivedRootJoin.java | 4 +- .../tree/domain/SqmCorrelatedEntityJoin.java | 7 +- .../tree/domain/SqmCorrelatedListJoin.java | 4 +- .../sqm/tree/domain/SqmCorrelatedMapJoin.java | 4 +- .../domain/SqmCorrelatedPluralPartJoin.java | 4 +- .../sqm/tree/domain/SqmCorrelatedRoot.java | 4 +- .../tree/domain/SqmCorrelatedRootJoin.java | 4 +- .../sqm/tree/domain/SqmCorrelatedSetJoin.java | 4 +- .../domain/SqmCorrelatedSingularJoin.java | 4 +- .../query/sqm/tree/domain/SqmCteRoot.java | 4 +- .../query/sqm/tree/domain/SqmDerivedRoot.java | 4 +- .../domain/SqmElementAggregateFunction.java | 57 ++- .../domain/SqmEmbeddedValuedSimplePath.java | 6 +- .../domain/SqmEntityValuedSimplePath.java | 6 +- .../sqm/tree/domain/SqmFkExpression.java | 18 +- .../sqm/tree/domain/SqmFunctionPath.java | 45 +- .../sqm/tree/domain/SqmFunctionRoot.java | 4 +- .../domain/SqmIndexAggregateFunction.java | 57 ++- .../SqmIndexedCollectionAccessPath.java | 6 +- .../query/sqm/tree/domain/SqmListJoin.java | 4 +- .../sqm/tree/domain/SqmMapEntryReference.java | 2 +- .../query/sqm/tree/domain/SqmMapJoin.java | 4 +- .../query/sqm/tree/domain/SqmPath.java | 2 +- .../sqm/tree/domain/SqmPluralPartJoin.java | 4 +- .../domain/SqmPluralValuedSimplePath.java | 9 +- .../query/sqm/tree/domain/SqmSetJoin.java | 4 +- .../sqm/tree/domain/SqmSingularJoin.java | 4 +- .../sqm/tree/domain/SqmTreatedBagJoin.java | 4 +- .../SqmTreatedEmbeddedValuedSimplePath.java | 6 +- .../sqm/tree/domain/SqmTreatedEntityJoin.java | 4 +- .../SqmTreatedEntityValuedSimplePath.java | 4 +- .../sqm/tree/domain/SqmTreatedListJoin.java | 4 +- .../sqm/tree/domain/SqmTreatedMapJoin.java | 4 +- .../tree/domain/SqmTreatedPluralPartJoin.java | 24 +- .../query/sqm/tree/domain/SqmTreatedRoot.java | 2 +- .../sqm/tree/domain/SqmTreatedSetJoin.java | 4 +- .../sqm/tree/domain/SqmTreatedSimplePath.java | 4 +- .../tree/domain/SqmTreatedSingularJoin.java | 4 +- .../query/sqm/tree/expression/SqmAny.java | 2 +- .../expression/SqmAnyDiscriminatorValue.java | 2 +- .../tree/expression/SqmBinaryArithmetic.java | 2 +- .../sqm/tree/expression/SqmCaseSearched.java | 2 +- .../sqm/tree/expression/SqmCaseSimple.java | 2 +- .../sqm/tree/expression/SqmCoalesce.java | 2 +- .../sqm/tree/expression/SqmDistinct.java | 2 +- .../sqm/tree/expression/SqmEnumLiteral.java | 2 +- .../query/sqm/tree/expression/SqmEvery.java | 2 +- .../sqm/tree/expression/SqmFieldLiteral.java | 2 +- .../SqmJpaCriteriaParameterWrapper.java | 2 +- .../tree/expression/SqmJsonTableFunction.java | 2 +- .../expression/SqmJsonValueExpression.java | 2 +- .../query/sqm/tree/expression/SqmLiteral.java | 2 +- .../expression/SqmLiteralEmbeddableType.java | 2 +- .../tree/expression/SqmLiteralEntityType.java | 2 +- .../sqm/tree/expression/SqmLiteralNull.java | 2 +- .../SqmModifiedSubQueryExpression.java | 2 +- .../tree/expression/SqmNamedExpression.java | 2 +- .../tree/expression/SqmNamedParameter.java | 2 +- .../query/sqm/tree/expression/SqmOver.java | 2 +- .../sqm/tree/expression/SqmOverflow.java | 2 +- .../SqmParameterizedEntityType.java | 2 +- .../expression/SqmPositionalParameter.java | 2 +- .../SqmSelfRenderingExpression.java | 2 +- .../sqm/tree/expression/SqmSummarization.java | 2 +- .../sqm/tree/expression/SqmToDuration.java | 2 +- .../query/sqm/tree/expression/SqmTuple.java | 2 +- .../tree/expression/SqmUnaryOperation.java | 2 +- .../tree/expression/SqmXmlTableFunction.java | 2 +- .../query/sqm/tree/from/SqmCrossJoin.java | 7 +- .../query/sqm/tree/from/SqmCteJoin.java | 4 +- .../query/sqm/tree/from/SqmDerivedJoin.java | 4 +- .../query/sqm/tree/from/SqmEntityJoin.java | 7 +- .../query/sqm/tree/from/SqmFunctionJoin.java | 4 +- .../query/sqm/tree/from/SqmRoot.java | 4 +- .../sqm/tree/insert/SqmConflictClause.java | 2 +- .../tree/insert/SqmConflictUpdateAction.java | 2 +- .../tree/insert/SqmInsertSelectStatement.java | 2 +- .../tree/insert/SqmInsertValuesStatement.java | 2 +- .../tree/predicate/SqmInListPredicate.java | 2 +- .../predicate/SqmInSubQueryPredicate.java | 2 +- .../tree/select/SqmDynamicInstantiation.java | 2 +- .../tree/select/SqmJpaCompoundSelection.java | 2 +- .../query/sqm/tree/select/SqmQueryGroup.java | 2 +- .../query/sqm/tree/select/SqmQuerySpec.java | 2 +- .../sqm/tree/select/SqmSelectStatement.java | 2 +- .../query/sqm/tree/select/SqmSubQuery.java | 2 +- .../sqm/tree/update/SqmUpdateStatement.java | 8 +- 118 files changed, 613 insertions(+), 610 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPath.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPath.java index 3fbe0ec4040e..03b74253ba57 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPath.java @@ -29,7 +29,7 @@ protected AnyDiscriminatorSqmPath( @Override public AnyDiscriminatorSqmPath copy(SqmCopyContext context) { - final AnyDiscriminatorSqmPath existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddedDiscriminatorSqmPath.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddedDiscriminatorSqmPath.java index 65a3b05667d5..650f0c406415 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddedDiscriminatorSqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddedDiscriminatorSqmPath.java @@ -52,7 +52,7 @@ public EmbeddableDomainType getEmbeddableDomainType() { @Override public EmbeddedDiscriminatorSqmPath copy(SqmCopyContext context) { - final EmbeddedDiscriminatorSqmPath existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityDiscriminatorSqmPath.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityDiscriminatorSqmPath.java index 10fe6fa1b404..4abc6181c474 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityDiscriminatorSqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityDiscriminatorSqmPath.java @@ -64,7 +64,7 @@ public EntityMappingType getEntityDescriptor() { @Override public EntityDiscriminatorSqmPath copy(SqmCopyContext context) { - final EntityDiscriminatorSqmPath existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java index 265c9ea46e8e..e120e438de3a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java @@ -198,8 +198,7 @@ private SemanticPathPart resolvePath(String identifier, boolean isTerminal, SqmC if ( pathRootByExposedNavigable != null ) { // identifier is an "unqualified attribute reference" validateAsRoot( pathRootByExposedNavigable ); - final SqmPath sqmPath = - pathRootByExposedNavigable.get( identifier, true ); + final var sqmPath = pathRootByExposedNavigable.get( identifier, true ); return isTerminal ? sqmPath : new DomainPathPart( sqmPath ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java index 280f48f4445a..39aeed17f989 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java @@ -10,7 +10,6 @@ import org.hibernate.query.hql.spi.SemanticPathPart; import org.hibernate.query.hql.spi.SqmCreationProcessingState; import org.hibernate.query.hql.spi.SqmCreationState; -import org.hibernate.query.hql.spi.SqmPathRegistry; import org.hibernate.query.sqm.SqmJoinable; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.spi.SqmCreationHelper; @@ -124,7 +123,7 @@ public void consumeTreat(String entityName, boolean isTerminal) { private ConsumerDelegate resolveBase(String identifier, boolean isTerminal) { final SqmCreationProcessingState processingState = creationState.getCurrentProcessingState(); - final SqmPathRegistry pathRegistry = processingState.getPathRegistry(); + final var pathRegistry = processingState.getPathRegistry(); final SqmFrom pathRootByAlias = pathRegistry.findFromByAlias( identifier, true ); if ( pathRootByAlias != null ) { return resolveAlias( identifier, isTerminal, pathRootByAlias ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QuerySplitter.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QuerySplitter.java index b7c294e5577d..cca925e7b74f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QuerySplitter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QuerySplitter.java @@ -78,7 +78,7 @@ private static > S copyStatement( final SqmCopyContext context = SqmCopyContext.noParamCopyContext(); // Copy the statement replacing the root's unmapped polymorphic reference with // the concrete mapped descriptor entity domain type. - final SqmRoot path = context.registerCopy( + final var path = context.registerCopy( unmappedPolymorphicReference, new SqmRoot<>( mappedDescriptor, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmAggregateFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmAggregateFunction.java index 0465d242a535..87426e00faa3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmAggregateFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmAggregateFunction.java @@ -47,7 +47,7 @@ public SelfRenderingSqmAggregateFunction( @Override public SelfRenderingSqmAggregateFunction copy(SqmCopyContext context) { - final SelfRenderingSqmAggregateFunction existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmFunction.java index ea201483a9c3..16a0720ded37 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmFunction.java @@ -59,7 +59,7 @@ public SelfRenderingSqmFunction( @Override public SelfRenderingSqmFunction copy(SqmCopyContext context) { - final SelfRenderingSqmFunction existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmOrderedSetAggregateFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmOrderedSetAggregateFunction.java index 0853d952cc82..f566f16cbd9d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmOrderedSetAggregateFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmOrderedSetAggregateFunction.java @@ -63,7 +63,7 @@ public SelfRenderingSqmOrderedSetAggregateFunction( @Override public SelfRenderingSqmOrderedSetAggregateFunction copy(SqmCopyContext context) { - final SelfRenderingSqmOrderedSetAggregateFunction existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmSetReturningFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmSetReturningFunction.java index 07050852dc99..c1fcae0e7fe3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmSetReturningFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmSetReturningFunction.java @@ -63,7 +63,7 @@ public SelfRenderingSqmSetReturningFunction( @Override public SelfRenderingSqmSetReturningFunction copy(SqmCopyContext context) { - final SelfRenderingSqmSetReturningFunction existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmWindowFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmWindowFunction.java index 242fa87c2e7d..3bddb3a703ef 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmWindowFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmWindowFunction.java @@ -53,7 +53,7 @@ public SelfRenderingSqmWindowFunction( @Override public SelfRenderingSqmWindowFunction copy(SqmCopyContext context) { - final SelfRenderingSqmWindowFunction existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java index f96a66ad04ae..c522f55221dd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java @@ -79,7 +79,6 @@ import org.hibernate.query.sqm.SetOperator; import org.hibernate.query.sqm.SqmBindableType; import org.hibernate.query.sqm.SqmExpressible; -import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.SqmQuerySource; import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.sqm.TrimSpec; @@ -91,7 +90,6 @@ import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers; import org.hibernate.query.sqm.tree.SqmQuery; -import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.query.sqm.tree.cte.SqmCteStatement; import org.hibernate.query.sqm.tree.cte.SqmCteTableColumn; @@ -166,6 +164,7 @@ import static jakarta.persistence.metamodel.Type.PersistenceType.BASIC; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; import static org.hibernate.internal.CoreMessageLogger.CORE_LOGGER; import static org.hibernate.internal.util.collections.CollectionHelper.determineProperSizing; import static org.hibernate.query.internal.QueryHelper.highestPrecedenceType; @@ -216,7 +215,7 @@ public SqmCriteriaNodeBuilder( private Map, HibernateCriteriaBuilder> loadExtensions() { // load registered criteria builder extensions final Map, HibernateCriteriaBuilder> extensions = new HashMap<>(); - for ( CriteriaBuilderExtension extension : ServiceLoader.load( CriteriaBuilderExtension.class ) ) { + for ( var extension : ServiceLoader.load( CriteriaBuilderExtension.class ) ) { extensions.put( extension.getRegistrationKey(), extension.extend( this ) ); } return extensions; @@ -265,7 +264,7 @@ public BasicType getBooleanType() { @Override public BasicType getIntegerType() { - final BasicType integerType = this.integerType; + final var integerType = this.integerType; if ( integerType == null ) { return this.integerType = getTypeConfiguration().getBasicTypeRegistry() @@ -276,7 +275,7 @@ public BasicType getIntegerType() { @Override public BasicType getLongType() { - final BasicType longType = this.longType; + final var longType = this.longType; if ( longType == null ) { return this.longType = getTypeConfiguration().getBasicTypeRegistry() @@ -287,7 +286,7 @@ public BasicType getLongType() { @Override public BasicType getCharacterType() { - final BasicType characterType = this.characterType; + final var characterType = this.characterType; if ( characterType == null ) { return this.characterType = getTypeConfiguration().getBasicTypeRegistry() @@ -298,7 +297,7 @@ public BasicType getCharacterType() { @Override public BasicType getStringType() { - final BasicType stringType = this.stringType; + final var stringType = this.stringType; if ( stringType == null ) { return this.stringType = getTypeConfiguration().getBasicTypeRegistry() @@ -308,17 +307,19 @@ public BasicType getStringType() { } public FunctionReturnTypeResolver getSumReturnTypeResolver() { - final FunctionReturnTypeResolver resolver = sumReturnTypeResolver; + final var resolver = sumReturnTypeResolver; if ( resolver == null ) { - return this.sumReturnTypeResolver = new SumReturnTypeResolver( getTypeConfiguration() ); + return sumReturnTypeResolver = + new SumReturnTypeResolver( getTypeConfiguration() ); } return resolver; } public FunctionReturnTypeResolver getAvgReturnTypeResolver() { - final FunctionReturnTypeResolver resolver = avgReturnTypeResolver; + final var resolver = avgReturnTypeResolver; if ( resolver == null ) { - return this.avgReturnTypeResolver = new AvgFunction.ReturnTypeResolver( getTypeConfiguration() ); + return avgReturnTypeResolver = + new AvgFunction.ReturnTypeResolver( getTypeConfiguration() ); } return resolver; } @@ -349,10 +350,9 @@ public SqmSelectStatement createQuery(Class resultClass) { @Override public SqmSelectStatement createQuery(String hql, Class resultClass) { - final SqmStatement statement = - queryEngine.getHqlTranslator().translate( hql, resultClass ); - if ( statement instanceof SqmSelectStatement ) { - return new SqmSelectStatement<>( (SqmSelectStatement) statement ); + if ( queryEngine.getHqlTranslator().translate( hql, resultClass ) + instanceof SqmSelectStatement selectStatement ) { + return new SqmSelectStatement<>( selectStatement ); } else { throw new IllegalArgumentException("Not a 'select' statement"); @@ -415,10 +415,16 @@ public JpaCriteriaSelect union(CriteriaSelect left, Criteria if ( left instanceof Subquery ) { assert right instanceof Subquery; //noinspection unchecked - return setOperation( SetOperator.UNION, (Subquery) left, (Subquery) right ); + return setOperation( SetOperator.UNION, + (Subquery) left, + (Subquery) right ); + } + else { + //noinspection unchecked + return setOperation( SetOperator.UNION, + (JpaCriteriaQuery) left, + (JpaCriteriaQuery) right ); } - //noinspection unchecked - return setOperation( SetOperator.UNION, (JpaCriteriaQuery) left, (JpaCriteriaQuery) right ); } @Override @@ -433,8 +439,12 @@ public CriteriaSelect unionAll(CriteriaSelect left, Criteria //noinspection unchecked return setOperation( SetOperator.UNION_ALL, (Subquery) left, (Subquery) right ); } - //noinspection unchecked - return setOperation( SetOperator.UNION_ALL, (JpaCriteriaQuery) left, (JpaCriteriaQuery) right ); + else { + //noinspection unchecked + return setOperation( SetOperator.UNION_ALL, + (JpaCriteriaQuery) left, + (JpaCriteriaQuery) right ); + } } @Override @@ -447,10 +457,16 @@ public CriteriaSelect except(CriteriaSelect left, CriteriaSelect ri if ( left instanceof Subquery ) { assert right instanceof Subquery; //noinspection unchecked - return setOperation( SetOperator.EXCEPT, (Subquery) left, (Subquery) right ); + return setOperation( SetOperator.EXCEPT, + (Subquery) left, + (Subquery) right ); + } + else { + //noinspection unchecked + return setOperation( SetOperator.EXCEPT, + (JpaCriteriaQuery) left, + (JpaCriteriaQuery) right ); } - //noinspection unchecked - return setOperation( SetOperator.EXCEPT, (JpaCriteriaQuery) left, (JpaCriteriaQuery) right ); } @Override @@ -458,10 +474,16 @@ public CriteriaSelect exceptAll(CriteriaSelect left, CriteriaSelect if ( left instanceof Subquery ) { assert right instanceof Subquery; //noinspection unchecked - return setOperation( SetOperator.EXCEPT_ALL, (Subquery) left, (Subquery) right ); + return setOperation( SetOperator.EXCEPT_ALL, + (Subquery) left, + (Subquery) right ); + } + else { + //noinspection unchecked + return setOperation( SetOperator.EXCEPT_ALL, + (JpaCriteriaQuery) left, + (JpaCriteriaQuery) right ); } - //noinspection unchecked - return setOperation( SetOperator.EXCEPT_ALL, (JpaCriteriaQuery) left, (JpaCriteriaQuery) right ); } @Override @@ -474,12 +496,12 @@ private JpaCriteriaQuery setOperation( SetOperator operator, CriteriaQuery criteriaQuery, CriteriaQuery... queries) { - final Class resultType = (Class) criteriaQuery.getResultType(); + final var resultType = (Class) criteriaQuery.getResultType(); final List> queryParts = new ArrayList<>( queries.length + 1 ); final Map> cteStatements = new LinkedHashMap<>(); - final SqmSelectStatement selectStatement = (SqmSelectStatement) criteriaQuery; + final var selectStatement = (SqmSelectStatement) criteriaQuery; collectQueryPartsAndCtes( selectStatement, queryParts, cteStatements ); - for ( CriteriaQuery query : queries ) { + for ( var query : queries ) { if ( query.getResultType() != resultType ) { throw new IllegalArgumentException( "Result type of all operands must match" ); } @@ -499,12 +521,12 @@ private JpaSubQuery setOperation( SetOperator operator, Subquery subquery, Subquery... queries) { - final Class resultType = (Class) subquery.getResultType(); - final SqmQuery parent = (SqmQuery) subquery.getParent(); + final var resultType = (Class) subquery.getResultType(); + final var parent = (SqmQuery) subquery.getParent(); final List> queryParts = new ArrayList<>( queries.length + 1 ); final Map> cteStatements = new LinkedHashMap<>(); collectQueryPartsAndCtes( (SqmSelectQuery) subquery, queryParts, cteStatements ); - for ( Subquery query : queries ) { + for ( var query : queries ) { if ( query.getResultType() != resultType ) { throw new IllegalArgumentException( "Result type of all operands must match" ); } @@ -527,9 +549,9 @@ private void collectQueryPartsAndCtes( List> queryParts, Map> cteStatements) { queryParts.add( query.getQueryPart() ); - for ( SqmCteStatement cteStatement : query.getCteStatements() ) { + for ( var cteStatement : query.getCteStatements() ) { final String name = cteStatement.getCteTable().getCteName(); - final SqmCteStatement old = cteStatements.put( name, cteStatement ); + final var old = cteStatements.put( name, cteStatement ); if ( old != null && old != cteStatement ) { throw new IllegalArgumentException( String.format( "Different CTE with same name [%s] found in different set operands!", name ) @@ -545,7 +567,7 @@ public SqmExpression cast(JpaExpression expression, Class castTa @Override public SqmExpression cast(JpaExpression expression, JpaCastTarget castTarget) { - final SqmCastTarget sqmCastTarget = (SqmCastTarget) castTarget; + final var sqmCastTarget = (SqmCastTarget) castTarget; return getFunctionDescriptor( "cast" ).generateSqmExpression( asList( (SqmTypedNode) expression, sqmCastTarget ), sqmCastTarget.getType(), @@ -568,8 +590,10 @@ public SqmCastTarget castTarget(Class castTargetJavaType, int precisio return castTarget( castTargetJavaType, null, precision, scale ); } - private SqmCastTarget castTarget(Class castTargetJavaType, @Nullable Long length, @Nullable Integer precision, @Nullable Integer scale) { - final BasicType type = getTypeConfiguration().standardBasicTypeForJavaType( castTargetJavaType ); + private SqmCastTarget castTarget( + Class castTargetJavaType, + @Nullable Long length, @Nullable Integer precision, @Nullable Integer scale) { + final var type = getTypeConfiguration().standardBasicTypeForJavaType( castTargetJavaType ); return new SqmCastTarget<>( type, length, precision, scale, this ); } @@ -586,12 +610,13 @@ public final SqmPredicate wrap(Expression... expressions) { if ( expressions.length == 1 ) { return wrap( expressions[0] ); } - - final List predicates = new ArrayList<>( expressions.length ); - for ( Expression expression : expressions ) { - predicates.add( wrap( expression ) ); + else { + final List predicates = new ArrayList<>( expressions.length ); + for ( var expression : expressions ) { + predicates.add( wrap( expression ) ); + } + return new SqmJunctionPredicate( Predicate.BooleanOperator.AND, predicates, this ); } - return new SqmJunctionPredicate( Predicate.BooleanOperator.AND, predicates, this ); } @Override @@ -599,12 +624,13 @@ public SqmPredicate wrap(List> restrictions) { if ( restrictions.size() == 1 ) { return wrap( restrictions.get( 0 ) ); } - - final List predicates = new ArrayList<>( restrictions.size() ); - for ( Expression expression : restrictions ) { - predicates.add( wrap( expression ) ); + else { + final List predicates = new ArrayList<>( restrictions.size() ); + for ( var expression : restrictions ) { + predicates.add( wrap( expression ) ); + } + return new SqmJunctionPredicate( Predicate.BooleanOperator.AND, predicates, this ); } - return new SqmJunctionPredicate( Predicate.BooleanOperator.AND, predicates, this ); } @Override @SuppressWarnings("unchecked") @@ -618,8 +644,8 @@ public T unwrap(Class clazz) { @Override public SqmPath fk(Path path) { - final SqmPath sqmPath = (SqmPath) path; - final SqmPathSource toOneReference = sqmPath.getReferencedPathSource(); + final var sqmPath = (SqmPath) path; + final var toOneReference = sqmPath.getReferencedPathSource(); final boolean validToOneRef = toOneReference.getBindableType() == Bindable.BindableType.SINGULAR_ATTRIBUTE && toOneReference instanceof EntitySqmPathSource; @@ -683,9 +709,10 @@ private static JpaCriteriaQuery createUnionSet( SetOperator operator, CriteriaQuery left, CriteriaQuery right) { - assert operator == SetOperator.UNION || operator == SetOperator.UNION_ALL; - final SqmSelectStatement leftSqm = (SqmSelectStatement) left; - final SqmSelectStatement rightSqm = (SqmSelectStatement) right; + assert operator == SetOperator.UNION + || operator == SetOperator.UNION_ALL; + final var leftSqm = (SqmSelectStatement) left; + final var rightSqm = (SqmSelectStatement) right; // SqmQueryGroup is the UNION ALL between the two final SqmQueryGroup sqmQueryGroup = new SqmQueryGroup( @@ -718,9 +745,10 @@ private static JpaCriteriaQuery createIntersectSet( SetOperator operator, CriteriaQuery left, CriteriaQuery right) { - assert operator == SetOperator.INTERSECT || operator == SetOperator.INTERSECT_ALL; - final SqmSelectStatement leftSqm = (SqmSelectStatement) left; - final SqmSelectStatement rightSqm = (SqmSelectStatement) right; + assert operator == SetOperator.INTERSECT + || operator == SetOperator.INTERSECT_ALL; + final var leftSqm = (SqmSelectStatement) left; + final var rightSqm = (SqmSelectStatement) right; // SqmQueryGroup is the UNION ALL between the two final SqmQueryGroup sqmQueryGroup = new SqmQueryGroup( @@ -753,9 +781,10 @@ private static JpaCriteriaQuery createExceptSet( SetOperator operator, CriteriaQuery left, CriteriaQuery right) { - assert operator == SetOperator.EXCEPT || operator == SetOperator.EXCEPT_ALL; - final SqmSelectStatement leftSqm = (SqmSelectStatement) left; - final SqmSelectStatement rightSqm = (SqmSelectStatement) right; + assert operator == SetOperator.EXCEPT + || operator == SetOperator.EXCEPT_ALL; + final var leftSqm = (SqmSelectStatement) left; + final var rightSqm = (SqmSelectStatement) right; // SqmQueryGroup is the UNION ALL between the two final SqmQueryGroup sqmQueryGroup = new SqmQueryGroup( @@ -931,7 +960,7 @@ public SqmTuple tuple(SqmExpressible tupleType, SqmExpression... ex @Override @Deprecated(since = "7", forRemoval = true) public SqmTuple tuple(Class tupleType, List> expressions) { @SuppressWarnings("unchecked") - final SqmExpressible expressibleType = + final var expressibleType = tupleType == null || tupleType == Object[].class ? (SqmDomainType) getTypeConfiguration().resolveTupleType( expressions ) : (SqmEmbeddableDomainType) getDomainModel().embeddable( tupleType ); @@ -1017,7 +1046,7 @@ else if ( Map.class.equals( resultClass ) ) { */ private void checkMultiselect(List> selections) { final HashSet aliases = new HashSet<>( determineProperSizing( selections.size() ) ); - for ( Selection selection : selections ) { + for ( var selection : selections ) { if ( selection.isCompoundSelection() ) { final Class javaType = selection.getJavaType(); if ( javaType.isArray() ) { @@ -1050,7 +1079,7 @@ public SqmExpression avg(Expression argument) { @Override @SuppressWarnings("unchecked") public SqmExpression sum(Expression argument) { - final SqmTypedNode typedNode = (SqmTypedNode) argument; + final var typedNode = (SqmTypedNode) argument; return getFunctionDescriptor( "sum" ).generateSqmExpression( typedNode, (ReturnableType) typedNode.getExpressible().getSqmType(), @@ -1216,10 +1245,9 @@ public JpaExpression truncate(Expression x, Integer n) @Override public SqmExpression neg(Expression x) { - final SqmExpression sqmExpression = (SqmExpression) x; return new SqmUnaryOperation<>( UnaryArithmeticOperator.UNARY_MINUS, - sqmExpression, + (SqmExpression) x, getNodeBuilder() ); } @@ -1353,7 +1381,7 @@ private SqmExpression createSqmArithmeticNode( BinaryArithmeticOperator operator, SqmExpression leftHandExpression, SqmExpression rightHandExpression) { - final SqmExpressible arithmeticType = + final var arithmeticType = getTypeConfiguration() .resolveArithmeticType( leftHandExpression.getNodeType(), @@ -1548,17 +1576,16 @@ public SqmLiteral literal(@Nullable T value, @Nullable SqmExpression SqmLiteral createLiteral(T value, SqmBindableType expressible) { - if ( expressible.getExpressibleJavaType().isInstance( value ) ) { + final var javaType = expressible.getExpressibleJavaType(); + if ( javaType.isInstance( value ) ) { return new SqmLiteral<>( value, expressible, this ); } else { // Just like in HQL, we allow coercion of literal values to the inferred type - final T coercedValue = - expressible.getExpressibleJavaType() - .coerce( value, this::getTypeConfiguration ); + final Object coercedValue = javaType.coerce( value ); // ignore typeInferenceSource and fall back to the value type - return expressible.getExpressibleJavaType().isInstance( coercedValue ) - ? new SqmLiteral<>( coercedValue, expressible, this ) + return javaType.isInstance( coercedValue ) + ? new SqmLiteral<>( javaType.cast( coercedValue ), expressible, this ) : literal( value ); } } @@ -1581,7 +1608,8 @@ private BasicType resolveInferredType(T value) { final var type = ReflectHelper.getClass( value ); final var result = typeConfiguration.getBasicTypeForJavaType( type ); if ( result == null && value instanceof Enum enumValue ) { - return (BasicType) resolveEnumType( typeConfiguration, enumValue ); + return (BasicType) + resolveEnumType( typeConfiguration, enumValue ); } else { return result; @@ -1615,27 +1643,29 @@ public MappingMetamodelImplementor getMappingMetamodel() { @Override public List> literals(T[] values) { if ( values == null || values.length == 0 ) { - return Collections.emptyList(); + return emptyList(); } - - final List> literals = new ArrayList<>(); - for ( T value : values ) { - literals.add( literal( value ) ); + else { + final List> literals = new ArrayList<>(); + for ( T value : values ) { + literals.add( literal( value ) ); + } + return literals; } - return literals; } @Override public List> literals(List values) { if ( values == null || values.isEmpty() ) { - return Collections.emptyList(); + return emptyList(); } - - final List> literals = new ArrayList<>(); - for ( T value : values ) { - literals.add( literal( value ) ); + else { + final List> literals = new ArrayList<>(); + for ( T value : values ) { + literals.add( literal( value ) ); + } + return literals; } - return literals; } @Override @@ -1693,7 +1723,7 @@ public JpaCriteriaParameter parameter(Class paramClass) { public JpaCriteriaParameter parameter(Class paramClass, @Nullable String name) { final var basicType = getTypeConfiguration().getBasicTypeForJavaType( paramClass ); final boolean notBasic = basicType == null; - final BindableType parameterType = + final var parameterType = notBasic && Collection.class.isAssignableFrom( paramClass ) // a Collection-valued, multi-valued parameter ? new MultiValueParameterType<>( (Class) Collection.class ) @@ -1724,10 +1754,8 @@ public SqmExpression concat(List> expressions) { @Override public SqmExpression concat(Expression x, Expression y) { - final SqmExpression xSqmExpression = (SqmExpression) x; - final SqmExpression ySqmExpression = (SqmExpression) y; return getFunctionDescriptor( "concat" ).generateSqmExpression( - asList( xSqmExpression, ySqmExpression ), + asList( (SqmExpression) x, (SqmExpression) y ), null, getQueryEngine() ); @@ -1735,11 +1763,8 @@ public SqmExpression concat(Expression x, Expression y) @Override public SqmExpression concat(Expression x, String y) { - final SqmExpression xSqmExpression = (SqmExpression) x; - final SqmExpression ySqmExpression = value( y, xSqmExpression ); - return getFunctionDescriptor( "concat" ).generateSqmExpression( - asList( xSqmExpression, ySqmExpression ), + asList( (SqmExpression) x, value( y, (SqmExpression) x ) ), null, getQueryEngine() ); @@ -1747,11 +1772,8 @@ public SqmExpression concat(Expression x, String y) { @Override public SqmExpression concat(String x, Expression y) { - final SqmExpression ySqmExpression = (SqmExpression) y; - final SqmExpression xSqmExpression = value( x, ySqmExpression ); - return getFunctionDescriptor( "concat" ).generateSqmExpression( - asList( xSqmExpression, ySqmExpression ), + asList( value( x, (SqmExpression) y ), (SqmExpression) y ), null, getQueryEngine() ); @@ -1759,11 +1781,8 @@ public SqmExpression concat(String x, Expression y) { @Override public SqmExpression concat(String x, String y) { - final SqmExpression xSqmExpression = value( x ); - final SqmExpression ySqmExpression = value( y, xSqmExpression ); - return getFunctionDescriptor( "concat" ).generateSqmExpression( - asList( xSqmExpression, ySqmExpression ), + asList( value( x ), value( y, value( x ) ) ), null, getQueryEngine() ); @@ -1912,23 +1931,10 @@ private SqmFunction createLocateFunctionNode( SqmExpression source, SqmExpression pattern, @Nullable SqmExpression startPosition) { - final List> arguments; - if ( startPosition == null ) { - arguments = asList( - pattern, - source - ); - } - else { - arguments = asList( - pattern, - source, - startPosition - ); - } - return getFunctionDescriptor("locate").generateSqmExpression( - arguments, + startPosition == null + ? asList( pattern, source ) + : asList( pattern, source, startPosition ), null, getQueryEngine() ); @@ -2038,7 +2044,7 @@ public SqmPath version(Path path) { @Override public SqmFunction function(String name, Class type, Expression[] args) { - final BasicType resultType = getTypeConfiguration().standardBasicTypeForJavaType( type ); + final var resultType = getTypeConfiguration().standardBasicTypeForJavaType( type ); return getFunctionTemplate( name, resultType ).generateSqmExpression( expressionList( args ), resultType, @@ -2047,7 +2053,7 @@ public SqmFunction function(String name, Class type, Expression[] a } private SqmFunctionDescriptor getFunctionTemplate(String name, BasicType resultType) { - final SqmFunctionDescriptor functionTemplate = getFunctionDescriptor( name ); + final var functionTemplate = getFunctionDescriptor( name ); if ( functionTemplate == null ) { return new NamedSqmFunctionDescriptor( name, @@ -2064,14 +2070,15 @@ private SqmFunctionDescriptor getFunctionTemplate(String name, BasicType private static List> expressionList(Expression[] jpaExpressions) { if ( jpaExpressions == null || jpaExpressions.length == 0 ) { - return Collections.emptyList(); + return emptyList(); } - - final ArrayList> sqmExpressions = new ArrayList<>(); - for ( Expression jpaExpression : jpaExpressions ) { - sqmExpressions.add( (SqmExpression) jpaExpression ); + else { + final ArrayList> sqmExpressions = new ArrayList<>(); + for ( var jpaExpression : jpaExpressions ) { + sqmExpressions.add( (SqmExpression) jpaExpression ); + } + return sqmExpressions; } - return sqmExpressions; } @Override @@ -2126,8 +2133,11 @@ public SqmExpression value(@Nullable T value, @Nullable SqmExpression SqmExpression> collectionValue(Collection value, SqmExpression typeInferenceSource) { - return inlineValue( value ) ? collectionLiteral( value.toArray() ) : collectionValueParameter( value, typeInferenceSource ); + private SqmExpression> collectionValue( + Collection value, SqmExpression typeInferenceSource) { + return inlineValue( value ) + ? collectionLiteral( value.toArray() ) + : collectionValueParameter( value, typeInferenceSource ); } @Override @@ -2165,15 +2175,16 @@ else if ( bindableType instanceof SqmExpressible expressible ) { @Nullable X value, @Nullable SqmExpression typeInferenceSource, TypeConfiguration typeConfiguration) { - if ( typeInferenceSource != null ) { if ( typeInferenceSource instanceof BindableType ) { //noinspection unchecked return (BindableType) typeInferenceSource; } - final SqmBindableType nodeType = typeInferenceSource.getExpressible(); - if ( nodeType != null ) { - return nodeType; + else { + final var nodeType = typeInferenceSource.getExpressible(); + if ( nodeType != null ) { + return nodeType; + } } } @@ -2194,16 +2205,19 @@ private ValueBindJpaCriteriaParameter valueParameter(@Nullable T value, @ final var widerType = (BindableType) bindableType; return new ValueBindJpaCriteriaParameter<>( widerType, value, this ); } - final T coercedValue = - resolveExpressible( bindableType ).getExpressibleJavaType() - .coerce( value, this::getTypeConfiguration ); - // ignore typeInferenceSource and fall back to the value type - if ( isInstance( bindableType, coercedValue ) ) { - @SuppressWarnings("unchecked") // safe, we just checked - final var widerType = (BindableType) bindableType; - return new ValueBindJpaCriteriaParameter<>( widerType, coercedValue, this ); + else { + final var javaType = resolveExpressible( bindableType ).getExpressibleJavaType(); + final Object coercedValue = javaType.coerce( value ); + // ignore typeInferenceSource and fall back to the value type + if ( isInstance( bindableType, coercedValue ) ) { + @SuppressWarnings("unchecked") // safe, we just checked + final var widerType = (BindableType) bindableType; + return new ValueBindJpaCriteriaParameter<>( widerType, javaType.cast( coercedValue ), this ); + } + else { + return new ValueBindJpaCriteriaParameter<>( getParameterBindType( value ), value, this ); + } } - return new ValueBindJpaCriteriaParameter<>( getParameterBindType( value ), value, this ); } private ValueBindJpaCriteriaParameter> collectionValueParameter(Collection value, SqmExpression elementTypeInferenceSource) { @@ -2349,12 +2363,13 @@ public SqmPredicate and(Predicate... restrictions) { if ( restrictions == null || restrictions.length == 0 ) { return conjunction(); } - - final List predicates = new ArrayList<>( restrictions.length ); - for ( var expression : restrictions ) { - predicates.add( (SqmPredicate) expression ); + else { + final List predicates = new ArrayList<>( restrictions.length ); + for ( var expression : restrictions ) { + predicates.add( (SqmPredicate) expression ); + } + return new SqmJunctionPredicate( Predicate.BooleanOperator.AND, predicates, this ); } - return new SqmJunctionPredicate( Predicate.BooleanOperator.AND, predicates, this ); } @Override @@ -2362,12 +2377,13 @@ public SqmPredicate and(List restrictions) { if ( restrictions == null || restrictions.isEmpty() ) { return conjunction(); } - - final List predicates = new ArrayList<>( restrictions.size() ); - for ( var expression : restrictions ) { - predicates.add( (SqmPredicate) expression ); + else { + final List predicates = new ArrayList<>( restrictions.size() ); + for ( var expression : restrictions ) { + predicates.add( (SqmPredicate) expression ); + } + return new SqmJunctionPredicate( Predicate.BooleanOperator.AND, predicates, this ); } - return new SqmJunctionPredicate( Predicate.BooleanOperator.AND, predicates, this ); } @Override @@ -2385,12 +2401,13 @@ public SqmPredicate or(Predicate... restrictions) { if ( restrictions == null || restrictions.length == 0 ) { return disjunction(); } - - final List predicates = new ArrayList<>( restrictions.length ); - for ( var expression : restrictions ) { - predicates.add( (SqmPredicate) expression ); + else { + final List predicates = new ArrayList<>( restrictions.length ); + for ( var expression : restrictions ) { + predicates.add( (SqmPredicate) expression ); + } + return new SqmJunctionPredicate( Predicate.BooleanOperator.OR, predicates, this ); } - return new SqmJunctionPredicate( Predicate.BooleanOperator.OR, predicates, this ); } @Override @@ -2398,12 +2415,13 @@ public SqmPredicate or(List restrictions) { if ( restrictions == null || restrictions.isEmpty() ) { return disjunction(); } - - final List predicates = new ArrayList<>( restrictions.size() ); - for ( var expression : restrictions ) { - predicates.add( (SqmPredicate) expression ); + else { + final List predicates = new ArrayList<>( restrictions.size() ); + for ( var expression : restrictions ) { + predicates.add( (SqmPredicate) expression ); + } + return new SqmJunctionPredicate( Predicate.BooleanOperator.OR, predicates, this ); } - return new SqmJunctionPredicate( Predicate.BooleanOperator.OR, predicates, this ); } @Override @@ -2464,13 +2482,11 @@ public > SqmPredicate between(Expression> SqmPredicate between(Expression value, Y lower, Y upper) { - final SqmExpression valueExpression = (SqmExpression) value; - final SqmExpression lowerExpr = value( lower, valueExpression ); - final SqmExpression upperExpr = value( upper, valueExpression ); + final var valueExpression = (SqmExpression) value; return new SqmBetweenPredicate( valueExpression, - lowerExpr, - upperExpr, + value( lower, valueExpression ), + value( upper, valueExpression ), false, this ); @@ -2488,7 +2504,7 @@ public SqmPredicate equal(Expression x, Expression y) { @Override public SqmPredicate equal(Expression x, Object y) { - final SqmExpression yExpr = value( y, (SqmExpression) x ); + final var yExpr = value( y, (SqmExpression) x ); return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.EQUAL, @@ -2509,7 +2525,7 @@ public SqmPredicate notEqual(Expression x, Expression y) { @Override public SqmPredicate notEqual(Expression x, Object y) { - final SqmExpression yExpr = value( y, (SqmExpression) x ); + final var yExpr = value( y, (SqmExpression) x ); return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.NOT_EQUAL, @@ -2530,7 +2546,7 @@ public SqmPredicate distinctFrom(Expression x, Expression y) { @Override public SqmPredicate distinctFrom(Expression x, Object y) { - final SqmExpression yExpr = value( y, (SqmExpression) x ); + final var yExpr = value( y, (SqmExpression) x ); return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.DISTINCT_FROM, @@ -2551,7 +2567,7 @@ public SqmPredicate notDistinctFrom(Expression x, Expression y) { @Override public SqmPredicate notDistinctFrom(Expression x, Object y) { - final SqmExpression yExpr = value( y, (SqmExpression) x ); + final var yExpr = value( y, (SqmExpression) x ); return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.NOT_DISTINCT_FROM, @@ -2572,7 +2588,7 @@ public > SqmPredicate greaterThan(Expression> SqmPredicate greaterThan(Expression x, Y y) { - final SqmExpression yExpr = value( y, (SqmExpression) x ); + final var yExpr = value( y, (SqmExpression) x ); return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.GREATER_THAN, @@ -2593,7 +2609,7 @@ public > SqmPredicate greaterThanOrEqualTo(Expre @Override public > SqmPredicate greaterThanOrEqualTo(Expression x, Y y) { - final SqmExpression yExpr = value( y, (SqmExpression) x ); + final var yExpr = value( y, (SqmExpression) x ); return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.GREATER_THAN_OR_EQUAL, @@ -2614,7 +2630,7 @@ public > SqmPredicate lessThan(Expression> SqmPredicate lessThan(Expression x, Y y) { - final SqmExpression yExpr = value( y, (SqmExpression) x ); + final var yExpr = value( y, (SqmExpression) x ); return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.LESS_THAN, @@ -2635,7 +2651,7 @@ public > SqmPredicate lessThanOrEqualTo(Expressi @Override public > SqmPredicate lessThanOrEqualTo(Expression x, Y y) { - final SqmExpression yExpr = value( y, (SqmExpression) x ); + final var yExpr = value( y, (SqmExpression) x ); return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.LESS_THAN_OR_EQUAL, @@ -2656,7 +2672,7 @@ public SqmPredicate gt(Expression x, Expression x, Number y) { - final SqmExpression yExpr = value( y, (SqmExpression) x ); + final var yExpr = value( y, (SqmExpression) x ); return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.GREATER_THAN, @@ -2677,7 +2693,7 @@ public SqmPredicate ge(Expression x, Expression x, Number y) { - final SqmExpression yExpr = value( y, (SqmExpression) x ); + final var yExpr = value( y, (SqmExpression) x ); return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.GREATER_THAN_OR_EQUAL, @@ -2698,7 +2714,7 @@ public SqmPredicate lt(Expression x, Expression x, Number y) { - final SqmExpression yExpr = value( y, (SqmExpression) x ); + final var yExpr = value( y, (SqmExpression) x ); return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.LESS_THAN, @@ -2719,7 +2735,7 @@ public SqmPredicate le(Expression x, Expression x, Number y) { - final SqmExpression yExpr = value( y, (SqmExpression) x ); + final var yExpr = value( y, (SqmExpression) x ); return new SqmComparisonPredicate( (SqmExpression) x, ComparisonOperator.LESS_THAN_OR_EQUAL, @@ -3028,7 +3044,7 @@ public SqmInPredicate in(Expression expression) { @SuppressWarnings("unchecked") public SqmInPredicate in(Expression expression, Expression... values) { final List> listExpressions = new ArrayList<>( values.length ); - for ( Expression value : values ) { + for ( var value : values ) { listExpressions.add( (SqmExpression) value ); } return new SqmInListPredicate<>( (SqmExpression) expression, listExpressions, this ); @@ -3037,7 +3053,7 @@ public SqmInPredicate in(Expression expression, Expression SqmInPredicate in(Expression expression, T... values) { - final SqmExpression sqmExpression = (SqmExpression) expression; + final var sqmExpression = (SqmExpression) expression; final List> listExpressions = new ArrayList<>( values.length ); for ( T value : values ) { listExpressions.add( value( value, sqmExpression ) ); @@ -3048,7 +3064,7 @@ public SqmInPredicate in(Expression expression, T... values) @Override @SuppressWarnings("unchecked") public SqmInPredicate in(Expression expression, Collection values) { - final SqmExpression sqmExpression = (SqmExpression) expression; + final var sqmExpression = (SqmExpression) expression; final List> listExpressions = new ArrayList<>( values.size() ); for ( T value : values ) { listExpressions.add( value( value, sqmExpression ) ); @@ -3128,7 +3144,7 @@ public SqmFunction sql(String pattern, Class type, Expression... ar @Override public SqmFunction format(Expression datetime, String pattern) { - final SqmFormat sqmFormat = new SqmFormat( pattern, getStringType(), this ); + final var sqmFormat = new SqmFormat( pattern, getStringType(), this ); return getFunctionDescriptor( "format" ).generateSqmExpression( asList( (SqmExpression) datetime, sqmFormat ), null, @@ -3329,9 +3345,9 @@ public SqmFunction overlay( Expression replacement, Expression start, @Nullable Expression length) { - SqmExpression sqmString = (SqmExpression) string; - SqmExpression sqmReplacement = (SqmExpression) replacement; - SqmExpression sqmStart = (SqmExpression) start; + final var sqmString = (SqmExpression) string; + final var sqmReplacement = (SqmExpression) replacement; + final var sqmStart = (SqmExpression) start; return getFunctionDescriptor( "overlay" ).generateSqmExpression( ( length == null ? asList( sqmString, sqmReplacement, sqmStart ) @@ -3402,9 +3418,9 @@ public SqmFunction pad( Expression x, Expression length, @Nullable Expression padChar) { - SqmExpression source = (SqmExpression) x; - SqmExpression sqmLength = (SqmExpression) length; - SqmTrimSpecification padSpec = new SqmTrimSpecification( + final var source = (SqmExpression) x; + final var sqmLength = (SqmExpression) length; + final var padSpec = new SqmTrimSpecification( ts == null ? TrimSpec.TRAILING : fromCriteriaTrimSpec( ts ), this ); @@ -3883,24 +3899,21 @@ public SqmExpression functionWithinGroup( @Nullable JpaPredicate filter, @Nullable JpaWindow window, Expression... args) { - SqmOrderByClause withinGroupClause = new SqmOrderByClause(); + final var withinGroupClause = new SqmOrderByClause(); if ( order != null ) { withinGroupClause.addSortSpecification( (SqmSortSpecification) order ); } - SqmPredicate sqmFilter = filter != null ? (SqmPredicate) filter : null; - SqmExpression function = getFunctionDescriptor( name ).generateOrderedSetAggregateSqmExpression( - expressionList( args ), - sqmFilter, - withinGroupClause, - null, - queryEngine - ); - if ( window == null ) { - return function; - } - else { - return new SqmOver<>( function, (SqmWindow) window ); - } + final var sqmFilter = filter != null ? (SqmPredicate) filter : null; + final SqmExpression function = + getFunctionDescriptor( name ) + .generateOrderedSetAggregateSqmExpression( + expressionList( args ), + sqmFilter, + withinGroupClause, + null, + queryEngine + ); + return window == null ? function : new SqmOver<>( function, (SqmWindow) window ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 78483d6625b4..c70b1b74d6b0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -5813,7 +5813,7 @@ else if ( Character.class.isAssignableFrom( valueConverter.getRelationalJavaType else if ( Number.class.isAssignableFrom( valueConverter.getRelationalJavaType().getJavaTypeClass() ) && value instanceof Number ) { return valueConverter.getRelationalJavaType() - .coerce( value, creationContext::getTypeConfiguration ); + .coerce( value ); } else { throw new SemanticException( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmDmlStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmDmlStatement.java index 6e88b7a10c9c..a4a5a48dbdd5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmDmlStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmDmlStatement.java @@ -25,6 +25,8 @@ import java.util.Set; import java.util.function.Function; +import static java.lang.Character.isAlphabetic; + /** * @author Steve Ebersole */ @@ -56,17 +58,19 @@ public AbstractSqmDmlStatement( } protected Map> copyCteStatements(SqmCopyContext context) { - final Map> cteStatements = new LinkedHashMap<>( this.cteStatements.size() ); - for ( Map.Entry> entry : this.cteStatements.entrySet() ) { - cteStatements.put( entry.getKey(), entry.getValue().copy( context ) ); + final Map> copy = + new LinkedHashMap<>( cteStatements.size() ); + for ( var entry : cteStatements.entrySet() ) { + copy.put( entry.getKey(), entry.getValue().copy( context ) ); } - return cteStatements; + return copy; } protected void putAllCtes(SqmCteContainer cteContainer) { - for ( SqmCteStatement cteStatement : cteContainer.getCteStatements() ) { - if ( cteStatements.putIfAbsent( cteStatement.getCteTable().getCteName(), cteStatement ) != null ) { - throw new IllegalArgumentException( "A CTE with the label " + cteStatement.getCteTable().getCteName() + " already exists" ); + for ( var cteStatement : cteContainer.getCteStatements() ) { + final String cteName = cteStatement.getCteTable().getCteName(); + if ( cteStatements.putIfAbsent( cteName, cteStatement ) != null ) { + throw new IllegalArgumentException( "A CTE with the label " + cteName + " already exists" ); } } } @@ -138,7 +142,7 @@ private String validateCteName(String name) { if ( name == null || name.isBlank() ) { throw new IllegalArgumentException( "Illegal empty CTE name" ); } - if ( !Character.isAlphabetic( name.charAt( 0 ) ) ) { + if ( !isAlphabetic( name.charAt( 0 ) ) ) { throw new IllegalArgumentException( String.format( "Illegal CTE name [%s]. Names must start with an alphabetic character!", @@ -150,7 +154,7 @@ private String validateCteName(String name) { } private JpaCteCriteria withInternal(String name, AbstractQuery criteria) { - final SqmCteStatement cteStatement = new SqmCteStatement<>( + final var cteStatement = new SqmCteStatement<>( name, (SqmSelectQuery) criteria, this, @@ -167,7 +171,7 @@ private JpaCteCriteria withInternal( AbstractQuery baseCriteria, boolean unionDistinct, Function, AbstractQuery> recursiveCriteriaProducer) { - final SqmCteStatement cteStatement = new SqmCteStatement<>( + final var cteStatement = new SqmCteStatement<>( name, (SqmSelectQuery) baseCriteria, unionDistinct, @@ -199,7 +203,7 @@ public SqmSubQuery subquery(Class type) { protected void appendHqlCteString(StringBuilder sb, SqmRenderContext context) { if ( !cteStatements.isEmpty() ) { sb.append( "with " ); - for ( SqmCteStatement value : cteStatements.values() ) { + for ( var value : cteStatements.values() ) { value.appendHqlString( sb, context ); sb.append( ", " ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmRestrictedDmlStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmRestrictedDmlStatement.java index 82531d198607..44185863958d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmRestrictedDmlStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmRestrictedDmlStatement.java @@ -60,7 +60,7 @@ protected AbstractSqmRestrictedDmlStatement( return null; } else { - final SqmPredicate predicate = whereClause.getPredicate(); + final var predicate = whereClause.getPredicate(); return new SqmWhereClause( predicate == null ? null : predicate.copy( context ), nodeBuilder() ); } } @@ -70,8 +70,8 @@ public SqmRoot from(Class entityClass) { } public SqmRoot from(EntityType entity) { - final EntityDomainType entityDomainType = (EntityDomainType) entity; - final SqmRoot root = getTarget(); + final var entityDomainType = (EntityDomainType) entity; + final var root = getTarget(); if ( root.getModel() != entity ) { throw new IllegalArgumentException( String.format( @@ -124,7 +124,7 @@ protected void setWhere(Predicate @Nullable ... restrictions) { // Clear the current predicate if one is present whereClause.setPredicate( null ); if ( restrictions != null ) { - for ( Predicate restriction : restrictions ) { + for ( var restriction : restrictions ) { whereClause.applyPredicate( (SqmPredicate) restriction ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmStatement.java index be3d59fae475..83217d39c292 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/AbstractSqmStatement.java @@ -49,7 +49,7 @@ protected AbstractSqmStatement( } else { final Set> parameters = new LinkedHashSet<>( this.parameters.size() ); - for ( SqmParameter parameter : this.parameters ) { + for ( var parameter : this.parameters ) { parameters.add( parameter.copy( context ) ); } return parameters; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java index 4ab2ad036baa..dc476e8d841d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java @@ -147,7 +147,7 @@ private SqmCteStatement( @Override public SqmCteStatement copy(SqmCopyContext context) { - final SqmCteStatement existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/delete/SqmDeleteStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/delete/SqmDeleteStatement.java index d2ddd024a301..136783583b11 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/delete/SqmDeleteStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/delete/SqmDeleteStatement.java @@ -61,7 +61,7 @@ public SqmDeleteStatement( @Override public SqmDeleteStatement copy(SqmCopyContext context) { - final SqmDeleteStatement existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java index 76f83749a12a..243382d20741 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java @@ -85,7 +85,6 @@ protected AbstractSqmFrom( @Nullable String alias, NodeBuilder nodeBuilder) { super( navigablePath, referencedNavigable, lhs, nodeBuilder ); - if ( lhs == null ) { throw new IllegalArgumentException( "LHS cannot be null" ); } @@ -135,24 +134,34 @@ protected void copyTo(AbstractSqmFrom target, SqmCopyContext context) { super.copyTo( target, context ); final var joins = this.joins; if ( joins != null ) { - final ArrayList> newJoins = - new ArrayList<>( joins.size() ); - for ( var join : joins ) { - newJoins.add( join.copy( context ) ); - } - target.joins = newJoins; + target.joins = copyJoins( context, joins ); } final var treats = this.treats; if ( treats != null ) { - final ArrayList> newTreats = - new ArrayList<>( treats.size() ); - for ( SqmTreatedFrom treat : treats ) { - newTreats.add( treat.copy( context ) ); - } - target.treats = newTreats; + target.treats = copyTreats( context, treats ); } } + private static ArrayList> copyTreats( + SqmCopyContext context, List> treats) { + final ArrayList> newTreats = + new ArrayList<>( treats.size() ); + for ( SqmTreatedFrom treat : treats ) { + newTreats.add( treat.copy( context ) ); + } + return newTreats; + } + + private static ArrayList> copyJoins( + SqmCopyContext context, List> joins) { + final ArrayList> newJoins = + new ArrayList<>( joins.size() ); + for ( var join : joins ) { + newJoins.add( join.copy( context ) ); + } + return newJoins; + } + @Override public @Nullable String getExplicitAlias() { return alias; @@ -203,7 +212,7 @@ public SqmPath resolvePathPart( if ( resolvedPath != null ) { return resolvedPath; } - final SqmPath sqmPath = get( name, true ); + final var sqmPath = get( name, true ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); return sqmPath; } @@ -234,22 +243,21 @@ public void addSqmJoin(SqmJoin join) { @Internal public void removeLeftFetchJoins() { - final List> joins = this.joins; + final var joins = this.joins; if ( joins != null ) { for ( var join : new ArrayList<>( joins ) ) { - if ( join instanceof SqmAttributeJoin attributeJoin ) { - if ( attributeJoin.isFetched() ) { - if ( join.getSqmJoinType() == SqmJoinType.LEFT ) { - joins.remove( join ); - final var orderedJoins = findRoot().getOrderedJoins(); - if ( orderedJoins != null ) { - orderedJoins.remove( join ); - } - } - else { - attributeJoin.clearFetched(); + if ( join instanceof SqmAttributeJoin attributeJoin + && attributeJoin.isFetched() ) { + if ( join.getSqmJoinType() == SqmJoinType.LEFT ) { + joins.remove( join ); + final var orderedJoins = findRoot().getOrderedJoins(); + if ( orderedJoins != null ) { + orderedJoins.remove( join ); } } + else { + attributeJoin.clearFetched(); + } } } } @@ -267,7 +275,8 @@ public void visitSqmJoins(Consumer> consumer) { return treats == null ? emptyList() : treats; } - protected > @Nullable X findTreat(ManagedDomainType targetType, @Nullable String alias) { + protected > @Nullable X findTreat( + ManagedDomainType targetType, @Nullable String alias) { if ( treats != null ) { for ( var treat : treats ) { if ( treat.getModel() == targetType ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmJoin.java index f0bc2d5ceb4d..dbee0745575e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmJoin.java @@ -57,20 +57,18 @@ public void setJoinPredicate(@Nullable SqmPredicate predicate) { LOG.tracef( "Setting join predicate [%s] (was [%s])", predicate, - this.onClausePredicate == null ? "" : this.onClausePredicate + onClausePredicate == null ? "" : this.onClausePredicate ); } - this.onClausePredicate = predicate; + onClausePredicate = predicate; } public void applyRestriction(SqmPredicate restriction) { - if ( this.onClausePredicate == null ) { - this.onClausePredicate = restriction; - } - else { - this.onClausePredicate = combinePredicates( onClausePredicate, restriction ); - } + onClausePredicate = + onClausePredicate == null + ? restriction + : combinePredicates( onClausePredicate, restriction ); } protected void copyTo(AbstractSqmJoin target, SqmCopyContext context) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java index 41963d633b34..d8dab6a5801a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java @@ -16,7 +16,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.AssertionFailure; import org.hibernate.metamodel.mapping.CollectionPart; -import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.metamodel.model.domain.PersistentAttribute; @@ -38,6 +37,7 @@ import static java.util.Collections.emptyList; import static org.hibernate.internal.util.NullnessUtil.castNonNull; +import static org.hibernate.metamodel.mapping.EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME; /** * @author Steve Ebersole @@ -73,22 +73,27 @@ protected void copyTo(AbstractSqmPath target, SqmCopyContext context) { // meant for assertions only private boolean navigablePathsMatch(AbstractSqmPath target) { - final SqmPath lhs = getLhsOrRoot(); - final SqmPath targetLhs = target.getLhsOrRoot(); + final var lhs = getLhsOrRoot(); + final var targetLhs = target.getLhsOrRoot(); return lhs == null && targetLhs == null - || lhs != null && targetLhs != null - && (lhs.getNavigablePath() == targetLhs.getNavigablePath() - || getRoot( lhs ).getNodeType() instanceof SqmPolymorphicRootDescriptor - ); + || lhs != null && targetLhs != null && ( samePath( lhs, targetLhs ) || isPolymorphicRoot( lhs ) ); + } + + private static boolean samePath(SqmPath lhs, SqmPath targetLhs) { + return lhs.getNavigablePath() == targetLhs.getNavigablePath(); + } + + private boolean isPolymorphicRoot(SqmPath lhs) { + return getRoot( lhs ).getNodeType() instanceof SqmPolymorphicRootDescriptor; } private @Nullable SqmPath getLhsOrRoot() { - final SqmPath lhs = getLhs(); + final var lhs = getLhs(); return lhs != null ? lhs : findRoot(); } private SqmPath getRoot(SqmPath lhs) { - final SqmPath parent = lhs.getLhs(); + final var parent = lhs.getLhs(); return parent == null ? lhs : getRoot( parent ); } @@ -131,9 +136,9 @@ public void registerReusablePath(SqmPath path) { reusablePaths = new HashMap<>(); } final String relativeName = path.getNavigablePath().getLocalName(); - final SqmPath previous = reusablePaths.put( relativeName, path ); + final var previous = reusablePaths.put( relativeName, path ); if ( previous != null && previous != path ) { - throw new IllegalStateException( "Implicit-join path registration unexpectedly overrode previous registration - " + relativeName ); + throw new IllegalStateException( "Implicit join path registration unexpectedly overrode previous registration - " + relativeName ); } } @@ -159,11 +164,12 @@ public SqmPathSource getModel() { @Override public SqmPathSource getResolvedModel() { - final SqmPathSource pathSource = getReferencedPathSource(); - final SqmPath lhs = getLhs(); + final var pathSource = getReferencedPathSource(); + final var lhs = getLhs(); if ( pathSource.isGeneric() && lhs != null && lhs.getResolvedModel().getPathType() instanceof SqmManagedDomainType lhsType ) { - final var concreteAttribute = lhsType.findConcreteGenericAttribute( pathSource.getPathName() ); + final var concreteAttribute = + lhsType.findConcreteGenericAttribute( pathSource.getPathName() ); if ( concreteAttribute != null ) { //noinspection unchecked return (SqmPathSource) concreteAttribute; @@ -189,34 +195,31 @@ public SqmPathSource getResolvedModel() { @Override public SqmExpression> type() { - final SqmPathSource referencedPathSource = getReferencedPathSource(); - final SqmPathSource subPathSource = - referencedPathSource.findSubPathSource( EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME ); + final var referencedPathSource = getReferencedPathSource(); + final var subPathSource = + referencedPathSource.findSubPathSource( DISCRIMINATOR_ROLE_NAME ); if ( subPathSource == null ) { - return new SqmLiteral<>( - referencedPathSource.getBindableJavaType(), + return new SqmLiteral<>( referencedPathSource.getBindableJavaType(), nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( Class.class ), - nodeBuilder() - ); + nodeBuilder() ); } else { @SuppressWarnings("unchecked") final var discriminatorSource = (SqmPathSource>) subPathSource; - return resolvePath( EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME, discriminatorSource ); + return resolvePath( DISCRIMINATOR_ROLE_NAME, discriminatorSource ); } } @Override public SqmPath get(String attributeName) { @SuppressWarnings("unchecked") - final SqmPathSource subNavigable = (SqmPathSource) getResolvedModel().getSubPathSource( attributeName ); + final var subNavigable = (SqmPathSource) getResolvedModel().getSubPathSource( attributeName ); return resolvePath( attributeName, subNavigable ); } @Override - public SqmPath get(String attributeName, boolean includeSubtypes) { - @SuppressWarnings("unchecked") - final SqmPathSource subPathSource = (SqmPathSource) + public SqmPath get(String attributeName, boolean includeSubtypes) { + final var subPathSource = (SqmPathSource) getResolvedModel().getSubPathSource( attributeName, includeSubtypes ); return resolvePath( attributeName, subPathSource ); } @@ -227,11 +230,12 @@ protected SqmPath resolvePath(PersistentAttribute attribute) { } protected SqmPath resolvePath(String attributeName, SqmPathSource pathSource) { - final SqmPathSource intermediatePathSource = - getResolvedModel().getIntermediatePathSource( pathSource ); + final var intermediatePathSource = + getResolvedModel() + .getIntermediatePathSource( pathSource ); if ( reusablePaths == null ) { reusablePaths = new HashMap<>(); - final SqmPath path = pathSource.createSqmPath( this, intermediatePathSource ); + final var path = pathSource.createSqmPath( this, intermediatePathSource ); reusablePaths.put( attributeName, path ); return path; } @@ -244,28 +248,29 @@ protected SqmPath resolvePath(String attributeName, SqmPathSource path } protected SqmTreatedPath getTreatedPath(ManagedDomainType treatTarget) { - final NavigablePath treat = getNavigablePath().treatAs( treatTarget.getTypeName() ); - final SqmPath lhs = castNonNull( getLhs() ); - final SqmPath reusablePath = lhs.getReusablePath( treat.getLocalName() ); - //TODO: check this cast - @SuppressWarnings("unchecked") - final SqmTreatedPath path = (SqmTreatedPath) reusablePath; + final var treat = getNavigablePath().treatAs( treatTarget.getTypeName() ); + final var lhs = castNonNull( getLhs() ); + final var path = lhs.getReusablePath( treat.getLocalName() ); if ( path == null ) { - final SqmTreatedPath treatedPath; - if ( treatTarget instanceof SqmEntityDomainType entityDomainType ) { - treatedPath = new SqmTreatedEntityValuedSimplePath<>( this, entityDomainType, nodeBuilder() ); - } - else if ( treatTarget instanceof SqmEmbeddableDomainType embeddableDomainType ) { - treatedPath = new SqmTreatedEmbeddedValuedSimplePath<>( this, embeddableDomainType ); - } - else { - throw new AssertionFailure( "Unrecognized treat target type: " + treatTarget.getTypeName() ); - } + final var treatedPath = treat( treatTarget ); lhs.registerReusablePath( treatedPath ); return treatedPath; } else { - return path; + //TODO: check this cast + return (SqmTreatedPath) path; + } + } + + private SqmTreatedPath treat(ManagedDomainType treatTarget) { + if ( treatTarget instanceof SqmEntityDomainType entityDomainType ) { + return new SqmTreatedEntityValuedSimplePath<>( this, entityDomainType, nodeBuilder() ); + } + else if ( treatTarget instanceof SqmEmbeddableDomainType embeddableDomainType ) { + return new SqmTreatedEmbeddedValuedSimplePath<>( this, embeddableDomainType ); + } + else { + throw new AssertionFailure( "Unrecognized treat target type: " + treatTarget.getTypeName() ); } } @@ -304,10 +309,9 @@ public SqmTreatedPath treatAs(EntityDomainType treatTarge * and if not creates a copy of the navigable path with the correct parent. */ protected NavigablePath getNavigablePathCopy(SqmPath parent) { - final NavigablePath realParentPath = getRealParentPath( - castNonNull( navigablePath.getRealParent() ), - parent.getNavigablePath() - ); + final var realParentPath = + getRealParentPath( castNonNull( navigablePath.getRealParent() ), + parent.getNavigablePath() ); if ( realParentPath != null ) { return realParentPath.append( navigablePath.getLocalName(), navigablePath.getAlias() ); } @@ -315,38 +319,34 @@ protected NavigablePath getNavigablePathCopy(SqmPath parent) { } private @Nullable NavigablePath getRealParentPath(NavigablePath realParent, NavigablePath parent) { - @Nullable NavigablePath realParentPath; if ( parent == realParent ) { - realParentPath = null; + return null; } - else if ( realParent instanceof EntityIdentifierNavigablePath entityIdentifierNavigablePath ) { - realParentPath = getRealParentPath( castNonNull( realParent.getRealParent() ), parent ); - if ( realParentPath != null ) { - realParentPath = new EntityIdentifierNavigablePath( - realParentPath, - entityIdentifierNavigablePath.getIdentifierAttributeName() - ); + else { + final var realParentParent = realParent.getRealParent(); + if ( realParent instanceof EntityIdentifierNavigablePath entityIdentifierNavigablePath ) { + final var realParentPath = getRealParentPath( castNonNull( realParentParent ), parent ); + return realParentPath != null + ? new EntityIdentifierNavigablePath( realParentPath, + entityIdentifierNavigablePath.getIdentifierAttributeName() ) + : null; } - } - else if ( realParent.getAlias() == null && realParent instanceof TreatedNavigablePath ) { - // This might be an implicitly treated parent path, check with the non-treated parent - realParentPath = getRealParentPath( castNonNull( realParent.getRealParent() ), parent ); - if ( realParentPath != null ) { - realParentPath = realParentPath.treatAs( realParent.getLocalName().substring( 1 ) ); + else if ( realParent instanceof TreatedNavigablePath && realParent.getAlias() == null ) { + // This might be an implicitly treated parent path, check with the non-treated parent + final var realParentPath = getRealParentPath( castNonNull( realParentParent ), parent ); + return realParentPath != null + ? realParentPath.treatAs( realParent.getLocalName().substring( 1 ) ) + : null; } - } - else if ( CollectionPart.Nature.fromNameExact( realParent.getLocalName() ) != null ) { - if ( parent == realParent.getRealParent() ) { - realParentPath = null; + else if ( CollectionPart.Nature.fromNameExact( realParent.getLocalName() ) != null ) { + return parent == realParentParent + ? null + : parent.append( realParent.getLocalName() ); } else { - realParentPath = parent.append( realParent.getLocalName() ); + return parent; } } - else { - realParentPath = parent; - } - return realParentPath; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmSimplePath.java index 5a2508e074d2..14acc0b67729 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmSimplePath.java @@ -63,7 +63,7 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public SqmPathSource getReferencedPathSource() { - final SqmPathSource pathSource = super.getReferencedPathSource(); + final var pathSource = super.getReferencedPathSource(); return pathSource instanceof SqmSingularPersistentAttribute attribute ? attribute.getSqmPathSource() : pathSource; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/NonAggregatedCompositeSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/NonAggregatedCompositeSimplePath.java index 0336e139d421..38a4755dec23 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/NonAggregatedCompositeSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/NonAggregatedCompositeSimplePath.java @@ -32,13 +32,13 @@ public NonAggregatedCompositeSimplePath( @Override public NonAggregatedCompositeSimplePath copy(SqmCopyContext context) { - final NonAggregatedCompositeSimplePath existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmPath lhsCopy = getLhs().copy( context ); - final NonAggregatedCompositeSimplePath path = context.registerCopy( + final var lhsCopy = getLhs().copy( context ); + final var path = context.registerCopy( this, new NonAggregatedCompositeSimplePath<>( getNavigablePathCopy( lhsCopy ), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmAnyValuedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmAnyValuedSimplePath.java index a9cb529f5843..a9ecb6eb5602 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmAnyValuedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmAnyValuedSimplePath.java @@ -42,13 +42,13 @@ public SqmAnyValuedSimplePath( @Override public SqmAnyValuedSimplePath copy(SqmCopyContext context) { - final SqmAnyValuedSimplePath existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmPath lhsCopy = getLhs().copy( context ); - final SqmAnyValuedSimplePath path = context.registerCopy( + final var lhsCopy = getLhs().copy( context ); + final var path = context.registerCopy( this, new SqmAnyValuedSimplePath<>( getNavigablePathCopy( lhsCopy ), @@ -97,7 +97,7 @@ public SqmPath resolvePathPart( String name, boolean isTerminal, SqmCreationState creationState) { - final SqmPath sqmPath = get( name, true ); + final var sqmPath = get( name, true ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); return sqmPath; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBagJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBagJoin.java index 56f0734df782..0261947e3b76 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBagJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBagJoin.java @@ -51,12 +51,12 @@ protected SqmBagJoin( @Override public SqmBagJoin copy(SqmCopyContext context) { - final SqmBagJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmFrom lhsCopy = getLhs().copy( context ); - final SqmBagJoin path = context.registerCopy( + final var lhsCopy = getLhs().copy( context ); + final var path = context.registerCopy( this, new SqmBagJoin<>( lhsCopy, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBasicValuedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBasicValuedSimplePath.java index fd9da0ce5153..63a5e462e3a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBasicValuedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBasicValuedSimplePath.java @@ -8,7 +8,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.model.domain.EntityDomainType; -import org.hibernate.query.hql.spi.SqmPathRegistry; import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.sqm.SqmBindableType; import org.hibernate.query.sqm.UnknownPathException; @@ -54,13 +53,13 @@ public SqmBasicValuedSimplePath( @Override public SqmBasicValuedSimplePath copy(SqmCopyContext context) { - final SqmBasicValuedSimplePath existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmPath lhsCopy = getLhs().copy( context ); - final SqmBasicValuedSimplePath path = context.registerCopy( + final var lhsCopy = getLhs().copy( context ); + final var path = context.registerCopy( this, new SqmBasicValuedSimplePath<>( getNavigablePathCopy( lhsCopy ), @@ -105,7 +104,7 @@ public SqmPath resolveIndexedAccess( SqmExpression selector, boolean isTerminal, SqmCreationState creationState) { - final SqmPathRegistry pathRegistry = + final var pathRegistry = creationState.getCurrentProcessingState().getPathRegistry(); final String alias = selector.toHqlString(); final NavigablePath navigablePath = diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedBagJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedBagJoin.java index db109cab8805..70b1f411f211 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedBagJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedBagJoin.java @@ -50,11 +50,11 @@ private SqmCorrelatedBagJoin( @Override public SqmCorrelatedBagJoin copy(SqmCopyContext context) { - final SqmCorrelatedBagJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCorrelatedBagJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCorrelatedBagJoin<>( getLhs().copy( context ), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCrossJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCrossJoin.java index f0b3250a89fc..e3865dab2c08 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCrossJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCrossJoin.java @@ -6,7 +6,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.query.hql.spi.SqmCreationProcessingState; -import org.hibernate.query.hql.spi.SqmPathRegistry; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.from.SqmCrossJoin; @@ -45,11 +44,11 @@ private SqmCorrelatedCrossJoin( @Override public SqmCorrelatedCrossJoin copy(SqmCopyContext context) { - final SqmCorrelatedCrossJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCorrelatedCrossJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCorrelatedCrossJoin<>( getReferencedPathSource(), @@ -85,7 +84,7 @@ public SqmRoot getCorrelatedRoot() { @Override public SqmCorrelatedCrossJoin makeCopy(SqmCreationProcessingState creationProcessingState) { - final SqmPathRegistry pathRegistry = creationProcessingState.getPathRegistry(); + final var pathRegistry = creationProcessingState.getPathRegistry(); return new SqmCorrelatedCrossJoin<>( getReferencedPathSource(), getExplicitAlias(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCteJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCteJoin.java index fdec8d254534..736874882396 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCteJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedCteJoin.java @@ -51,11 +51,11 @@ private SqmCorrelatedCteJoin( @Override public SqmCorrelatedCteJoin copy(SqmCopyContext context) { - final SqmCorrelatedCteJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCorrelatedCteJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCorrelatedCteJoin<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedJoin.java index 31f4f204d265..730d2ba7331f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedJoin.java @@ -55,11 +55,11 @@ private SqmCorrelatedDerivedJoin( @Override public SqmCorrelatedDerivedJoin copy(SqmCopyContext context) { - final SqmCorrelatedDerivedJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCorrelatedDerivedJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCorrelatedDerivedJoin<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedRoot.java index 9a1e35a0b96f..d31a92765f97 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedRoot.java @@ -32,11 +32,11 @@ private SqmCorrelatedDerivedRoot(SqmRoot correlationParent) { @Override public SqmCorrelatedDerivedRoot copy(SqmCopyContext context) { - final SqmCorrelatedDerivedRoot existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCorrelatedDerivedRoot path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCorrelatedDerivedRoot<>( getCorrelationParent().copy( context ) ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedRootJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedRootJoin.java index 3e26123b3b11..a9507348376e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedRootJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedDerivedRootJoin.java @@ -26,11 +26,11 @@ public SqmCorrelatedDerivedRootJoin( @Override public SqmCorrelatedDerivedRootJoin copy(SqmCopyContext context) { - final SqmCorrelatedDerivedRootJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCorrelatedDerivedRootJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCorrelatedDerivedRootJoin<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedEntityJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedEntityJoin.java index 3cc0ae64d453..10741af724a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedEntityJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedEntityJoin.java @@ -7,7 +7,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.query.hql.spi.SqmCreationProcessingState; -import org.hibernate.query.hql.spi.SqmPathRegistry; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmJoinType; @@ -49,11 +48,11 @@ public SqmCorrelatedEntityJoin( @Override public SqmCorrelatedEntityJoin copy(SqmCopyContext context) { - final SqmCorrelatedEntityJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCorrelatedEntityJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCorrelatedEntityJoin<>( getReferencedPathSource(), @@ -105,7 +104,7 @@ public SqmCorrelatedEntityJoin createCorrelation() { @Override public SqmCorrelatedEntityJoin makeCopy(SqmCreationProcessingState creationProcessingState) { - final SqmPathRegistry pathRegistry = creationProcessingState.getPathRegistry(); + final var pathRegistry = creationProcessingState.getPathRegistry(); return new SqmCorrelatedEntityJoin<>( getReferencedPathSource(), getExplicitAlias(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedListJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedListJoin.java index fc9b039bcca7..6b5804e61690 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedListJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedListJoin.java @@ -50,11 +50,11 @@ private SqmCorrelatedListJoin( @Override public SqmCorrelatedListJoin copy(SqmCopyContext context) { - final SqmCorrelatedListJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCorrelatedListJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCorrelatedListJoin<>( getLhs().copy( context ), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedMapJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedMapJoin.java index 0bb8c3b42bb2..9d07a56777b4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedMapJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedMapJoin.java @@ -50,11 +50,11 @@ private SqmCorrelatedMapJoin( @Override public SqmCorrelatedMapJoin copy(SqmCopyContext context) { - final SqmCorrelatedMapJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCorrelatedMapJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCorrelatedMapJoin<>( getLhs().copy( context ), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedPluralPartJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedPluralPartJoin.java index 496c55a9c44d..1d5b2ebeed43 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedPluralPartJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedPluralPartJoin.java @@ -33,11 +33,11 @@ public SqmCorrelatedPluralPartJoin(SqmPluralPartJoin correlationParent) { @Override public SqmCorrelatedPluralPartJoin copy(SqmCopyContext context) { - final SqmCorrelatedPluralPartJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCorrelatedPluralPartJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCorrelatedPluralPartJoin<>( correlationParent.copy( context ) ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java index 0d0824267277..40969d7fd864 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRoot.java @@ -38,11 +38,11 @@ protected SqmCorrelatedRoot(NavigablePath navigablePath, SqmPathSource refere @Override public SqmCorrelatedRoot copy(SqmCopyContext context) { - final SqmCorrelatedRoot existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCorrelatedRoot path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCorrelatedRoot<>( correlationParent.copy( context ) ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRootJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRootJoin.java index 0a77643f4ee5..314d58fdbf2c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRootJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedRootJoin.java @@ -28,11 +28,11 @@ public SqmCorrelatedRootJoin( @Override public SqmCorrelatedRootJoin copy(SqmCopyContext context) { - final SqmCorrelatedRootJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCorrelatedRootJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCorrelatedRootJoin<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSetJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSetJoin.java index 21abbcec1ecc..999ca4e304b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSetJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSetJoin.java @@ -50,11 +50,11 @@ private SqmCorrelatedSetJoin( @Override public SqmCorrelatedSetJoin copy(SqmCopyContext context) { - final SqmCorrelatedSetJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCorrelatedSetJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCorrelatedSetJoin<>( getLhs().copy( context ), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSingularJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSingularJoin.java index 3db5f80c14eb..a83133c851d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSingularJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCorrelatedSingularJoin.java @@ -50,11 +50,11 @@ private SqmCorrelatedSingularJoin( @Override public SqmCorrelatedSingularJoin copy(SqmCopyContext context) { - final SqmCorrelatedSingularJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCorrelatedSingularJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCorrelatedSingularJoin<>( getLhs().copy( context ), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java index a1d5973902d9..850a56e27aed 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmCteRoot.java @@ -54,11 +54,11 @@ protected SqmCteRoot( @Override public SqmCteRoot copy(SqmCopyContext context) { - final SqmCteRoot existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCteRoot path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCteRoot<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java index 05ea4470d4be..ba28e6da5df5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmDerivedRoot.java @@ -55,11 +55,11 @@ protected SqmDerivedRoot( @Override public SqmDerivedRoot copy(SqmCopyContext context) { - final SqmDerivedRoot existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmDerivedRoot path = context.registerCopy( + final var path = context.registerCopy( this, new SqmDerivedRoot<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmElementAggregateFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmElementAggregateFunction.java index cb46ed519897..c099cd2079c6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmElementAggregateFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmElementAggregateFunction.java @@ -13,7 +13,6 @@ import org.hibernate.query.hql.spi.SqmCreationState; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmBindableType; -import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; @@ -38,32 +37,28 @@ public SqmElementAggregateFunction(SqmPluralValuedSimplePath pluralDomainPath .getElementPathSource() ); this.functionName = functionName; - final SqmCriteriaNodeBuilder nodeBuilder = pluralDomainPath.nodeBuilder(); - switch ( functionName ) { - case "sum": - //noinspection unchecked - this.returnableType = (ReturnableType) nodeBuilder.getSumReturnTypeResolver() - .resolveFunctionReturnType( - null, - (SqmToSqlAstConverter) null, - List.of( pluralDomainPath ), - nodeBuilder.getTypeConfiguration() - ); - break; - case "avg": - //noinspection unchecked - this.returnableType = (ReturnableType) nodeBuilder.getAvgReturnTypeResolver() - .resolveFunctionReturnType( - null, - (SqmToSqlAstConverter) null, - List.of( pluralDomainPath ), - nodeBuilder.getTypeConfiguration() - ); - break; - default: - this.returnableType = null; - break; - } + final var nodeBuilder = pluralDomainPath.nodeBuilder(); + final var type = switch ( functionName ) { + case "sum" -> + nodeBuilder.getSumReturnTypeResolver() + .resolveFunctionReturnType( + null, + (SqmToSqlAstConverter) null, + List.of( pluralDomainPath ), + nodeBuilder.getTypeConfiguration() + ); + case "avg" -> + nodeBuilder.getAvgReturnTypeResolver() + .resolveFunctionReturnType( + null, + (SqmToSqlAstConverter) null, + List.of( pluralDomainPath ), + nodeBuilder.getTypeConfiguration() + ); + default -> null; + }; + //noinspection unchecked + returnableType = (ReturnableType) type; } @Override @@ -87,14 +82,14 @@ public SqmElementAggregateFunction(SqmPluralValuedSimplePath pluralDomainPath @Override public SqmElementAggregateFunction copy(SqmCopyContext context) { - final SqmElementAggregateFunction existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmElementAggregateFunction path = context.registerCopy( + final var path = context.registerCopy( this, - new SqmElementAggregateFunction<>( + new SqmElementAggregateFunction( getPluralDomainPath().copy( context ), functionName ) @@ -112,7 +107,7 @@ public SqmPath resolvePathPart( String name, boolean isTerminal, SqmCreationState creationState) { - final SqmPath sqmPath = get( name, true ); + final var sqmPath = get( name, true ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); return sqmPath; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmEmbeddedValuedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmEmbeddedValuedSimplePath.java index c84032b4457f..8e6ca7368536 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmEmbeddedValuedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmEmbeddedValuedSimplePath.java @@ -49,13 +49,13 @@ public SqmEmbeddedValuedSimplePath( @Override public SqmEmbeddedValuedSimplePath copy(SqmCopyContext context) { - final SqmEmbeddedValuedSimplePath existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } final SqmPath lhsCopy = getLhs().copy( context ); - final SqmEmbeddedValuedSimplePath path = context.registerCopy( + final var path = context.registerCopy( this, new SqmEmbeddedValuedSimplePath<>( getNavigablePathCopy( lhsCopy ), @@ -86,7 +86,7 @@ public PersistenceType getPersistenceType() { @Override public SqmPath resolvePathPart(String name, boolean isTerminal, SqmCreationState creationState) { - final SqmPath sqmPath = get( name, true ); + final var sqmPath = get( name, true ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); return sqmPath; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmEntityValuedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmEntityValuedSimplePath.java index a3ba68ecf625..f2adc59d68a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmEntityValuedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmEntityValuedSimplePath.java @@ -28,13 +28,13 @@ public SqmEntityValuedSimplePath( @Override public SqmEntityValuedSimplePath copy(SqmCopyContext context) { - final SqmEntityValuedSimplePath existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } final SqmPath lhsCopy = getLhs().copy( context ); - final SqmEntityValuedSimplePath path = context.registerCopy( + final var path = context.registerCopy( this, new SqmEntityValuedSimplePath<>( getNavigablePathCopy( lhsCopy ), @@ -52,7 +52,7 @@ public SqmPath resolvePathPart( String name, boolean isTerminal, SqmCreationState creationState) { - final SqmPath sqmPath = get( name, true ); + final var sqmPath = get( name, true ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); return sqmPath; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFkExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFkExpression.java index 73f18ff4d2bd..3d3085f48431 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFkExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFkExpression.java @@ -6,7 +6,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; -import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.IdentifiableDomainType; import org.hibernate.query.hql.spi.SqmCreationState; @@ -37,19 +36,22 @@ private SqmFkExpression( SqmPath toOnePath) { super( navigablePath, - (SqmPathSource) castNonNull( pathDomainType( toOnePath ).getIdentifierDescriptor() ), + (SqmPathSource) + castNonNull( pathDomainType( toOnePath ) + .getIdentifierDescriptor() ), toOnePath, toOnePath.nodeBuilder() ); } private static IdentifiableDomainType pathDomainType(SqmPath toOnePath) { - final DomainType domainType = toOnePath.getReferencedPathSource().getPathType(); - if ( domainType instanceof IdentifiableDomainType identifiableDomainType ) { + if ( toOnePath.getReferencedPathSource().getPathType() + instanceof IdentifiableDomainType identifiableDomainType ) { return identifiableDomainType; } else { - throw new IllegalArgumentException( "Invalid path provided to 'fk()' function: " + toOnePath.getNavigablePath() ); + throw new IllegalArgumentException( "Invalid path provided to 'fk()' function: " + + toOnePath.getNavigablePath() ); } } @@ -72,11 +74,11 @@ public void appendHqlString(StringBuilder hql, SqmRenderContext context) { @Override public SqmFkExpression copy(SqmCopyContext context) { - final SqmFkExpression existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmEntityValuedSimplePath lhsCopy = (SqmEntityValuedSimplePath) getLhs().copy( context ); + final var lhsCopy = (SqmEntityValuedSimplePath) getLhs().copy( context ); return context.registerCopy( this, new SqmFkExpression<>( getNavigablePathCopy( lhsCopy ), lhsCopy ) @@ -95,7 +97,7 @@ public SqmTreatedPath treatAs(EntityDomainType treatTarget @Override public SqmPath resolvePathPart(String name, boolean isTerminal, SqmCreationState creationState) { - final SqmPath sqmPath = get( name, true ); + final var sqmPath = get( name, true ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); return sqmPath; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionPath.java index 3ed186cdae57..5f5035dc36b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionPath.java @@ -10,8 +10,6 @@ import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource; import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource; import org.hibernate.query.hql.spi.SqmCreationState; -import org.hibernate.query.hql.spi.SqmPathRegistry; -import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmBindableType; import org.hibernate.query.sqm.SqmPathSource; @@ -21,13 +19,10 @@ import org.hibernate.query.sqm.tree.SqmRenderContext; import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.query.sqm.tree.expression.SqmFunction; -import org.hibernate.query.sqm.tree.from.SqmFrom; import org.hibernate.spi.NavigablePath; import org.hibernate.type.BasicPluralType; -import org.hibernate.type.BasicType; import jakarta.persistence.metamodel.Bindable; -import jakarta.persistence.metamodel.ManagedType; import jakarta.persistence.metamodel.Type; @@ -52,17 +47,18 @@ private SqmFunctionPath(NavigablePath navigablePath, SqmFunction function) { private static SqmPathSource determinePathSource(NavigablePath navigablePath, SqmFunction function) { //noinspection unchecked - final SqmBindableType nodeType = (SqmBindableType) function.getNodeType(); + final var nodeType = (SqmBindableType) function.getNodeType(); if ( nodeType == null ) { throw new IllegalArgumentException( "Null return type for function: " + function.getFunctionName() ); } - final Class bindableJavaType = nodeType.getJavaType(); - final ManagedType managedType = function.nodeBuilder() - .getJpaMetamodel() - .findManagedType( bindableJavaType ); + final var bindableJavaType = nodeType.getJavaType(); + final var managedType = + function.nodeBuilder().getJpaMetamodel() + .findManagedType( bindableJavaType ); if ( managedType == null ) { - final BasicType basicType = function.nodeBuilder().getTypeConfiguration() - .getBasicTypeForJavaType( bindableJavaType ); + final var basicType = + function.nodeBuilder().getTypeConfiguration() + .getBasicTypeForJavaType( bindableJavaType ); if ( basicType == null ) { throw new IllegalArgumentException( "Couldn't determine basic type for java type: " + bindableJavaType.getName() ); } @@ -95,14 +91,15 @@ public SqmFunction getFunction() { @Override public SqmFunctionPath copy(SqmCopyContext context) { - final SqmFunctionPath existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmFunctionPath path = context.registerCopy( + final var path = context.registerCopy( this, - new SqmFunctionPath<>( getNavigablePath(), (SqmFunction) function.copy( context ) ) + new SqmFunctionPath( getNavigablePath(), + (SqmFunction) function.copy( context ) ) ); copyTo( path, context ); return path; @@ -113,7 +110,7 @@ public SqmPath resolvePathPart( String name, boolean isTerminal, SqmCreationState creationState) { - final SqmPath sqmPath = get( name, true ); + final var sqmPath = get( name, true ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); return sqmPath; } @@ -123,20 +120,18 @@ public SqmPath resolveIndexedAccess( SqmExpression selector, boolean isTerminal, SqmCreationState creationState) { - final SqmPathRegistry pathRegistry = creationState.getCurrentProcessingState().getPathRegistry(); - final String alias = selector.toHqlString(); - final NavigablePath navigablePath = getNavigablePath().append( - CollectionPart.Nature.ELEMENT.getName(), - alias - ); - final SqmFrom indexedPath = pathRegistry.findFromByPath( navigablePath ); + final var pathRegistry = creationState.getCurrentProcessingState().getPathRegistry(); + final var navigablePath = + getNavigablePath().append( CollectionPart.Nature.ELEMENT.getName(), + selector.toHqlString() ); + final var indexedPath = pathRegistry.findFromByPath( navigablePath ); if ( indexedPath != null ) { return indexedPath; } if ( !( getReferencedPathSource().getPathType() instanceof BasicPluralType ) ) { throw new UnsupportedOperationException( "Index access is only supported for basic plural types." ); } - final QueryEngine queryEngine = creationState.getCreationContext().getQueryEngine(); + final var queryEngine = creationState.getCreationContext().getQueryEngine(); final SelfRenderingSqmFunction result = queryEngine.getSqmFunctionRegistry() .getFunctionDescriptor( "array_get" ) .generateSqmExpression( @@ -144,7 +139,7 @@ public SqmPath resolveIndexedAccess( null, queryEngine ); - final SqmFunctionPath path = new SqmFunctionPath<>( result ); + final SqmFunctionPath path = new SqmFunctionPath<>( result ); pathRegistry.register( path ); return path; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionRoot.java index a0464f45fdde..32535c433619 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmFunctionRoot.java @@ -53,11 +53,11 @@ protected SqmFunctionRoot( @Override public SqmFunctionRoot copy(SqmCopyContext context) { - final SqmFunctionRoot existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmFunctionRoot path = context.registerCopy( + final var path = context.registerCopy( this, new SqmFunctionRoot<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexAggregateFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexAggregateFunction.java index 268f7aa2e580..6baa4c16208b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexAggregateFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexAggregateFunction.java @@ -14,7 +14,6 @@ import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmBindableType; import org.hibernate.query.sqm.SqmPathSource; -import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmRenderContext; @@ -42,32 +41,28 @@ public SqmIndexAggregateFunction(SqmPluralValuedSimplePath pluralDomainPath, .getIndexPathSource() ); this.functionName = functionName; - final SqmCriteriaNodeBuilder nodeBuilder = pluralDomainPath.nodeBuilder(); - switch ( functionName ) { - case "sum": - //noinspection unchecked - this.returnableType = (ReturnableType) nodeBuilder.getSumReturnTypeResolver() - .resolveFunctionReturnType( - null, - (SqmToSqlAstConverter) null, - List.of( pluralDomainPath.get( CollectionPart.Nature.INDEX.getName() ) ), - nodeBuilder.getTypeConfiguration() - ); - break; - case "avg": - //noinspection unchecked - this.returnableType = (ReturnableType) nodeBuilder.getAvgReturnTypeResolver() - .resolveFunctionReturnType( - null, - (SqmToSqlAstConverter) null, - List.of( pluralDomainPath.get( CollectionPart.Nature.INDEX.getName() ) ), - nodeBuilder.getTypeConfiguration() - ); - break; - default: - this.returnableType = null; - break; - } + final var nodeBuilder = pluralDomainPath.nodeBuilder(); + final var type = switch ( functionName ) { + case "sum" -> + nodeBuilder.getSumReturnTypeResolver() + .resolveFunctionReturnType( + null, + (SqmToSqlAstConverter) null, + List.of( pluralDomainPath.get( CollectionPart.Nature.INDEX.getName() ) ), + nodeBuilder.getTypeConfiguration() + ); + case "avg" -> + nodeBuilder.getAvgReturnTypeResolver() + .resolveFunctionReturnType( + null, + (SqmToSqlAstConverter) null, + List.of( pluralDomainPath.get( CollectionPart.Nature.INDEX.getName() ) ), + nodeBuilder.getTypeConfiguration() + ); + default -> null; + }; + //noinspection unchecked + returnableType = (ReturnableType) type; } @Override @@ -91,14 +86,14 @@ public SqmIndexAggregateFunction(SqmPluralValuedSimplePath pluralDomainPath, @Override public SqmIndexAggregateFunction copy(SqmCopyContext context) { - final SqmIndexAggregateFunction existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmIndexAggregateFunction path = context.registerCopy( + final var path = context.registerCopy( this, - new SqmIndexAggregateFunction<>( + new SqmIndexAggregateFunction( getPluralDomainPath().copy( context ), functionName ) @@ -116,7 +111,7 @@ public SqmPath resolvePathPart( String name, boolean isTerminal, SqmCreationState creationState) { - final SqmPath sqmPath = get( name, true ); + final var sqmPath = get( name, true ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); return sqmPath; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexedCollectionAccessPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexedCollectionAccessPath.java index b0191e880af8..8519d243c25d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexedCollectionAccessPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmIndexedCollectionAccessPath.java @@ -46,13 +46,13 @@ public SqmIndexedCollectionAccessPath( @Override public SqmIndexedCollectionAccessPath copy(SqmCopyContext context) { - final SqmIndexedCollectionAccessPath existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } final SqmAttributeJoin lhsCopy = getLhs().copy( context ); - final SqmIndexedCollectionAccessPath path = context.registerCopy( + final var path = context.registerCopy( this, new SqmIndexedCollectionAccessPath( getNavigablePathCopy( lhsCopy ), @@ -78,7 +78,7 @@ public SqmPath resolvePathPart( String name, boolean isTerminal, SqmCreationState creationState) { - final SqmPath sqmPath = get( name, true ); + final var sqmPath = get( name, true ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); return sqmPath; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmListJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmListJoin.java index 8ec14102919a..0268f27bc865 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmListJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmListJoin.java @@ -55,12 +55,12 @@ protected SqmListJoin( @Override public SqmListJoin copy(SqmCopyContext context) { - final SqmListJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } final SqmFrom lhsCopy = getLhs().copy( context ); - final SqmListJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmListJoin<>( lhsCopy, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapEntryReference.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapEntryReference.java index b246031821ea..99ec573cc3fe 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapEntryReference.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapEntryReference.java @@ -56,7 +56,7 @@ public SqmMapEntryReference( @Override public SqmMapEntryReference copy(SqmCopyContext context) { - final SqmMapEntryReference existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapJoin.java index 5491d2b8ba70..434f752961f1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapJoin.java @@ -55,12 +55,12 @@ protected SqmMapJoin( @Override public SqmMapJoin copy(SqmCopyContext context) { - final SqmMapJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } final SqmFrom lhsCopy = getLhs().copy( context ); - final SqmMapJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmMapJoin<>( lhsCopy, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java index 40563779e159..7ea720558d49 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java @@ -164,7 +164,7 @@ default SqmPath resolveIndexedAccess( * * @see SqmPathSource#findSubPathSource(String, boolean) */ - default SqmPath get(String attributeName, boolean includeSubtypes) { + default SqmPath get(String attributeName, boolean includeSubtypes) { return get( attributeName ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPluralPartJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPluralPartJoin.java index 2e6d88eae773..029d9ae48a5a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPluralPartJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPluralPartJoin.java @@ -67,12 +67,12 @@ public boolean isImplicitlySelectable() { @Override public SqmPluralPartJoin copy(SqmCopyContext context) { - final SqmPluralPartJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } final SqmFrom lhsCopy = getLhs().copy( context ); - final SqmPluralPartJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmPluralPartJoin<>( lhsCopy, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPluralValuedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPluralValuedSimplePath.java index 50bdcdffd36b..4aec559e33dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPluralValuedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPluralValuedSimplePath.java @@ -14,7 +14,6 @@ import org.hibernate.query.NotIndexedCollectionException; import org.hibernate.query.PathException; import org.hibernate.query.hql.spi.SqmCreationState; -import org.hibernate.query.hql.spi.SqmPathRegistry; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmPathSource; @@ -58,13 +57,13 @@ public SqmPluralValuedSimplePath( @Override public SqmPluralValuedSimplePath copy(SqmCopyContext context) { - final SqmPluralValuedSimplePath existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } final SqmPath lhsCopy = getLhs().copy( context ); - final SqmPluralValuedSimplePath path = context.registerCopy( + final var path = context.registerCopy( this, new SqmPluralValuedSimplePath<>( getNavigablePathCopy( lhsCopy ), @@ -104,7 +103,7 @@ public SqmPath resolvePathPart( + "' refers to a collection and so element attribute '" + name + "' may not be referenced directly (use element() function)" ); } - final SqmPath sqmPath = get( name, true ); + final var sqmPath = get( name, true ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); return sqmPath; } @@ -114,7 +113,7 @@ public SqmPath resolveIndexedAccess( SqmExpression selector, boolean isTerminal, SqmCreationState creationState) { - final SqmPathRegistry pathRegistry = creationState.getCurrentProcessingState().getPathRegistry(); + final var pathRegistry = creationState.getCurrentProcessingState().getPathRegistry(); final String alias = selector.toHqlString(); final NavigablePath navigablePath = getParentNavigablePath() diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSetJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSetJoin.java index df28f49f1af1..81007629b943 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSetJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSetJoin.java @@ -53,12 +53,12 @@ protected SqmSetJoin( @Override public SqmSetJoin copy(SqmCopyContext context) { - final SqmSetJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } final SqmFrom lhsCopy = getLhs().copy( context ); - final SqmSetJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmSetJoin<>( lhsCopy, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSingularJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSingularJoin.java index a5300366ad8e..909a55c307a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSingularJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSingularJoin.java @@ -65,12 +65,12 @@ protected SqmSingularJoin( @Override public SqmSingularJoin copy(SqmCopyContext context) { - final SqmSingularJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } final SqmFrom lhsCopy = getLhs().copy( context ); - final SqmSingularJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmSingularJoin<>( lhsCopy, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java index e91085a45730..82219a2a04b5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java @@ -78,11 +78,11 @@ private SqmTreatedBagJoin( @Override public SqmTreatedBagJoin copy(SqmCopyContext context) { - final SqmTreatedBagJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmTreatedBagJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmTreatedBagJoin<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEmbeddedValuedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEmbeddedValuedSimplePath.java index a69680b47fae..224fce35d086 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEmbeddedValuedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEmbeddedValuedSimplePath.java @@ -55,11 +55,11 @@ private SqmTreatedEmbeddedValuedSimplePath( @Override public SqmTreatedEmbeddedValuedSimplePath copy(SqmCopyContext context) { - final SqmTreatedEmbeddedValuedSimplePath existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmTreatedEmbeddedValuedSimplePath path = context.registerCopy( + final var path = context.registerCopy( this, new SqmTreatedEmbeddedValuedSimplePath<>( getNavigablePath(), @@ -104,7 +104,7 @@ public X accept(SemanticQueryWalker walker) { @Override public SqmPath resolvePathPart(String name, boolean isTerminal, SqmCreationState creationState) { - final SqmPath sqmPath = get( name, true ); + final var sqmPath = get( name, true ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); return sqmPath; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityJoin.java index 202786f6d1ba..d2b5413df139 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityJoin.java @@ -60,11 +60,11 @@ private SqmTreatedEntityJoin( @Override public SqmTreatedEntityJoin copy(SqmCopyContext context) { - final SqmTreatedEntityJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmTreatedEntityJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmTreatedEntityJoin<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityValuedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityValuedSimplePath.java index 7081f2d5cef7..398ae2c8cf3c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityValuedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityValuedSimplePath.java @@ -77,12 +77,12 @@ private SqmTreatedEntityValuedSimplePath( @Override public SqmTreatedEntityValuedSimplePath copy(SqmCopyContext context) { - final SqmTreatedEntityValuedSimplePath existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmTreatedEntityValuedSimplePath path = context.registerCopy( + final var path = context.registerCopy( this, new SqmTreatedEntityValuedSimplePath<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java index eea6d196161f..9f72b682d51b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java @@ -78,11 +78,11 @@ private SqmTreatedListJoin( @Override public SqmTreatedListJoin copy(SqmCopyContext context) { - final SqmTreatedListJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmTreatedListJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmTreatedListJoin<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java index 631a27f08278..930bb707e04d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java @@ -77,11 +77,11 @@ private SqmTreatedMapJoin( @Override public SqmTreatedMapJoin copy(SqmCopyContext context) { - final SqmTreatedMapJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmTreatedMapJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmTreatedMapJoin<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedPluralPartJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedPluralPartJoin.java index 3d53621d86a3..29b3a19b7e90 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedPluralPartJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedPluralPartJoin.java @@ -18,12 +18,12 @@ */ @SuppressWarnings("rawtypes") public class SqmTreatedPluralPartJoin extends SqmPluralPartJoin implements SqmTreatedJoin { - private final SqmPluralPartJoin wrappedPath; - private final SqmEntityDomainType treatTarget; + private final SqmPluralPartJoin wrappedPath; + private final SqmEntityDomainType treatTarget; public SqmTreatedPluralPartJoin( - SqmPluralPartJoin wrappedPath, - SqmEntityDomainType treatTarget, + SqmPluralPartJoin wrappedPath, + SqmEntityDomainType treatTarget, @Nullable String alias) { //noinspection unchecked super( @@ -41,8 +41,8 @@ public SqmTreatedPluralPartJoin( private SqmTreatedPluralPartJoin( NavigablePath navigablePath, - SqmPluralPartJoin wrappedPath, - SqmEntityDomainType treatTarget, + SqmPluralPartJoin wrappedPath, + SqmEntityDomainType treatTarget, @Nullable String alias) { //noinspection unchecked super( @@ -59,11 +59,11 @@ private SqmTreatedPluralPartJoin( @Override public SqmTreatedPluralPartJoin copy(SqmCopyContext context) { - final SqmTreatedPluralPartJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmTreatedPluralPartJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmTreatedPluralPartJoin( getNavigablePath(), @@ -77,22 +77,22 @@ public SqmTreatedPluralPartJoin copy(SqmCopyContext context) { } @Override - public SqmPluralPartJoin getWrappedPath() { + public SqmPluralPartJoin getWrappedPath() { return wrappedPath; } @Override - public EntityDomainType getTreatTarget() { + public EntityDomainType getTreatTarget() { return treatTarget; } @Override - public @NonNull SqmBindableType getNodeType() { + public @NonNull SqmBindableType getNodeType() { return treatTarget; } @Override - public SqmPathSource getReferencedPathSource() { + public SqmPathSource getReferencedPathSource() { return treatTarget; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedRoot.java index 1279851a7657..bee6babd11b5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedRoot.java @@ -116,7 +116,7 @@ public SqmPath resolvePathPart( String name, boolean isTerminal, SqmCreationState creationState) { - final SqmPath sqmPath = get( name, true ); + final var sqmPath = get( name, true ); creationState.getProcessingStateStack().getCurrent().getPathRegistry().register( sqmPath ); return sqmPath; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java index 05c249fed839..d90c44e0e8eb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java @@ -78,11 +78,11 @@ private SqmTreatedSetJoin( @Override public SqmTreatedSetJoin copy(SqmCopyContext context) { - final SqmTreatedSetJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmTreatedSetJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmTreatedSetJoin<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSimplePath.java index 63193e925e8d..505373053ddd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSimplePath.java @@ -78,12 +78,12 @@ private SqmTreatedSimplePath( @Override public SqmTreatedSimplePath copy(SqmCopyContext context) { - final SqmTreatedSimplePath existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmTreatedSimplePath path = context.registerCopy( + final var path = context.registerCopy( this, new SqmTreatedSimplePath<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java index df52fec73f8c..53fb588cdc98 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java @@ -80,11 +80,11 @@ private SqmTreatedSingularJoin( @Override public SqmTreatedSingularJoin copy(SqmCopyContext context) { - final SqmTreatedSingularJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmTreatedSingularJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmTreatedSingularJoin<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java index e1270c827e78..8a0b7ac7d523 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAny.java @@ -38,7 +38,7 @@ public SqmAny(SqmSubQuery subquery, NodeBuilder criteriaBuilder) { @Override public SqmAny copy(SqmCopyContext context) { - final SqmAny existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAnyDiscriminatorValue.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAnyDiscriminatorValue.java index b9675b66f913..ef8b45ffb9cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAnyDiscriminatorValue.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmAnyDiscriminatorValue.java @@ -43,7 +43,7 @@ public BasicType getDomainType(){ @Override public SqmAnyDiscriminatorValue copy(SqmCopyContext context) { - final SqmAnyDiscriminatorValue existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmBinaryArithmetic.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmBinaryArithmetic.java index 5e77fd159523..c98fca2883fc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmBinaryArithmetic.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmBinaryArithmetic.java @@ -73,7 +73,7 @@ public SqmBinaryArithmetic( @Override public SqmBinaryArithmetic copy(SqmCopyContext context) { - final SqmBinaryArithmetic existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java index 20461b1fb63e..1044d8447914 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSearched.java @@ -49,7 +49,7 @@ private SqmCaseSearched(@Nullable SqmBindableType inherentType, int estimated @Override public SqmCaseSearched copy(SqmCopyContext context) { - final SqmCaseSearched existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java index 593bb0f69d58..a8aceefad745 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCaseSimple.java @@ -55,7 +55,7 @@ private SqmCaseSimple( @Override public SqmCaseSimple copy(SqmCopyContext context) { - final SqmCaseSimple existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCoalesce.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCoalesce.java index 979ba811c726..431676cee9b7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCoalesce.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmCoalesce.java @@ -49,7 +49,7 @@ public SqmCoalesce(@Nullable SqmBindableType type, int numberOfArguments, Nod @Override public SqmCoalesce copy(SqmCopyContext context) { - final SqmCoalesce existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmDistinct.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmDistinct.java index ebd0ccc5f160..89c3af5cab83 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmDistinct.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmDistinct.java @@ -28,7 +28,7 @@ public SqmDistinct(SqmExpression expression, NodeBuilder builder) { @Override public SqmDistinct copy(SqmCopyContext context) { - final SqmDistinct existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java index 453b5a92b6e2..52fe2112efbc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java @@ -47,7 +47,7 @@ public SqmEnumLiteral( @Override public SqmEnumLiteral copy(SqmCopyContext context) { - final SqmEnumLiteral existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java index ca9096abb773..df6449272146 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEvery.java @@ -37,7 +37,7 @@ public SqmEvery(SqmSubQuery subquery, NodeBuilder criteriaBuilder) { @Override public SqmEvery copy(SqmCopyContext context) { - final SqmEvery existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java index f871671f8878..e197e1046a81 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java @@ -78,7 +78,7 @@ public PersistenceType getPersistenceType() { @Override public SqmFieldLiteral copy(SqmCopyContext context) { - final SqmFieldLiteral existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJpaCriteriaParameterWrapper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJpaCriteriaParameterWrapper.java index 2e9a35ef9801..35851e900791 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJpaCriteriaParameterWrapper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJpaCriteriaParameterWrapper.java @@ -46,7 +46,7 @@ public SqmJpaCriteriaParameterWrapper( @Override public SqmJpaCriteriaParameterWrapper copy(SqmCopyContext context) { - final SqmJpaCriteriaParameterWrapper existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonTableFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonTableFunction.java index 5df34f9b30aa..db519eaf445f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonTableFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonTableFunction.java @@ -138,7 +138,7 @@ public JpaJsonTableFunction passing(String parameterName, Expression expressi @Override public SqmJsonTableFunction copy(SqmCopyContext context) { - final SqmJsonTableFunction existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonValueExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonValueExpression.java index 5123d55348e1..48e778703c13 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonValueExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmJsonValueExpression.java @@ -99,7 +99,7 @@ private SqmJsonValueExpression( } public SqmJsonValueExpression copy(SqmCopyContext context) { - final SqmJsonValueExpression existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteral.java index ede9c60a6245..a271286ce9b2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteral.java @@ -53,7 +53,7 @@ protected SqmLiteral(@Nullable SqmBindableType inherentType, NodeBuilder node @Override public SqmLiteral copy(SqmCopyContext context) { - final SqmLiteral existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEmbeddableType.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEmbeddableType.java index 0fc44ed3fed3..02f5dbe2be02 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEmbeddableType.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEmbeddableType.java @@ -45,7 +45,7 @@ public EmbeddableDomainType getEmbeddableDomainType() { @Override public SqmLiteralEmbeddableType copy(SqmCopyContext context) { - final SqmLiteralEmbeddableType existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEntityType.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEntityType.java index 4792516c1c95..75be501c5a3d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEntityType.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEntityType.java @@ -42,7 +42,7 @@ public SqmLiteralEntityType(SqmEntityDomainType entityType, NodeBuilder nodeB @Override public SqmLiteralEntityType copy(SqmCopyContext context) { - final SqmLiteralEntityType existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralNull.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralNull.java index 8353603cfc4e..d4f05b1b9f3b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralNull.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralNull.java @@ -26,7 +26,7 @@ public SqmLiteralNull(@Nullable SqmBindableType expressibleType, NodeBuilder @Override public SqmLiteralNull copy(SqmCopyContext context) { - final SqmLiteralNull existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmModifiedSubQueryExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmModifiedSubQueryExpression.java index 5cefb33c02e9..b385d1299a22 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmModifiedSubQueryExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmModifiedSubQueryExpression.java @@ -53,7 +53,7 @@ public SqmModifiedSubQueryExpression( @Override public SqmModifiedSubQueryExpression copy(SqmCopyContext context) { - final SqmModifiedSubQueryExpression existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedExpression.java index 578e108979e9..d2cf343ace33 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedExpression.java @@ -32,7 +32,7 @@ public SqmNamedExpression(SqmExpression expression, String name) { @Override public SqmNamedExpression copy(SqmCopyContext context) { - final SqmNamedExpression existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedParameter.java index 3b847e1df9d3..e3b4064d1885 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedParameter.java @@ -36,7 +36,7 @@ public SqmNamedParameter( @Override public SqmNamedParameter copy(SqmCopyContext context) { - final SqmNamedParameter existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOver.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOver.java index d46895a9bd0c..b2e746a32acd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOver.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOver.java @@ -63,7 +63,7 @@ public SqmOver( @Override public SqmOver copy(SqmCopyContext context) { - final SqmOver existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOverflow.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOverflow.java index 94a29334b9b9..8b50156714a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOverflow.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmOverflow.java @@ -30,7 +30,7 @@ public SqmOverflow(SqmExpression separatorExpression, @Nullable SqmExpression @Override public SqmOverflow copy(SqmCopyContext context) { - final SqmOverflow existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameterizedEntityType.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameterizedEntityType.java index 915a506b3cb5..2fe415aeaedf 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameterizedEntityType.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameterizedEntityType.java @@ -32,7 +32,7 @@ public SqmParameterizedEntityType(SqmParameter parameterExpression, NodeBuild @Override public SqmParameterizedEntityType copy(SqmCopyContext context) { - final SqmParameterizedEntityType existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmPositionalParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmPositionalParameter.java index cea1a470b3b3..24a9096ecea9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmPositionalParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmPositionalParameter.java @@ -38,7 +38,7 @@ public SqmPositionalParameter( @Override public SqmPositionalParameter copy(SqmCopyContext context) { - final SqmPositionalParameter existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSelfRenderingExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSelfRenderingExpression.java index 3ba9155f13fa..387063aeb4d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSelfRenderingExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSelfRenderingExpression.java @@ -30,7 +30,7 @@ public SqmSelfRenderingExpression( @Override public SqmSelfRenderingExpression copy(SqmCopyContext context) { - final SqmSelfRenderingExpression existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSummarization.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSummarization.java index ce744035a9a7..eedb11bfe222 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSummarization.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmSummarization.java @@ -31,7 +31,7 @@ public SqmSummarization(Kind kind, List> groupings, NodeBuilder @Override public SqmSummarization copy(SqmCopyContext context) { - final SqmSummarization existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmToDuration.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmToDuration.java index f0917b6126e0..ea6ad1aef28b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmToDuration.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmToDuration.java @@ -35,7 +35,7 @@ public SqmToDuration( @Override public SqmToDuration copy(SqmCopyContext context) { - final SqmToDuration existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmTuple.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmTuple.java index dc3d93543df0..4a71852f3151 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmTuple.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmTuple.java @@ -61,7 +61,7 @@ public SqmTuple(List> groupedExpressions, @Nullable SqmBindable @Override public SqmTuple copy(SqmCopyContext context) { - final SqmTuple existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmUnaryOperation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmUnaryOperation.java index 211fd039c889..7a73098017e8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmUnaryOperation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmUnaryOperation.java @@ -49,7 +49,7 @@ public SqmUnaryOperation( @Override public SqmUnaryOperation copy(SqmCopyContext context) { - final SqmUnaryOperation existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmXmlTableFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmXmlTableFunction.java index ba8de20e513f..6e17d367b172 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmXmlTableFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmXmlTableFunction.java @@ -97,7 +97,7 @@ private static List> createArgumentsList(SqmExpression x @Override public SqmXmlTableFunction copy(SqmCopyContext context) { - final SqmXmlTableFunction existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCrossJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCrossJoin.java index 97a8ef98d811..3b79ff44bf03 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCrossJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCrossJoin.java @@ -9,7 +9,6 @@ import org.hibernate.metamodel.model.domain.PersistentAttribute; import org.hibernate.query.criteria.JpaCrossJoin; import org.hibernate.query.hql.spi.SqmCreationProcessingState; -import org.hibernate.query.hql.spi.SqmPathRegistry; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmJoinType; @@ -93,11 +92,11 @@ public SqmJoinType getSqmJoinType() { @Override public SqmCrossJoin copy(SqmCopyContext context) { - final SqmCrossJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmCrossJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCrossJoin<>( getNavigablePath(), @@ -145,7 +144,7 @@ public SqmCorrelatedCrossJoin createCorrelation() { } public SqmCrossJoin makeCopy(SqmCreationProcessingState creationProcessingState) { - final SqmPathRegistry pathRegistry = creationProcessingState.getPathRegistry(); + final var pathRegistry = creationProcessingState.getPathRegistry(); return new SqmCrossJoin<>( getReferencedPathSource(), getExplicitAlias(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCteJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCteJoin.java index ffa77988f79e..8e8d50a1b62d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCteJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCteJoin.java @@ -74,12 +74,12 @@ public boolean isImplicitlySelectable() { @Override public SqmCteJoin copy(SqmCopyContext context) { - final SqmCteJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } //noinspection unchecked - final SqmCteJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmCteJoin<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmDerivedJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmDerivedJoin.java index 35d4efe519cd..7f81dcce5508 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmDerivedJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmDerivedJoin.java @@ -97,12 +97,12 @@ public boolean isImplicitlySelectable() { @Override public SqmDerivedJoin copy(SqmCopyContext context) { - final SqmDerivedJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } //noinspection unchecked - final SqmDerivedJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmDerivedJoin<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmEntityJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmEntityJoin.java index e677689700bf..71fa83c1a766 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmEntityJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmEntityJoin.java @@ -11,7 +11,6 @@ import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaPredicate; import org.hibernate.query.hql.spi.SqmCreationProcessingState; -import org.hibernate.query.hql.spi.SqmPathRegistry; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.spi.SqmCreationHelper; import org.hibernate.query.sqm.tree.SqmCopyContext; @@ -76,11 +75,11 @@ public boolean isImplicitlySelectable() { @Override public SqmEntityJoin copy(SqmCopyContext context) { - final SqmEntityJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmEntityJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmEntityJoin<>( getNavigablePath(), @@ -210,7 +209,7 @@ public SqmCorrelatedEntityJoin createCorrelation() { } public SqmEntityJoin makeCopy(SqmCreationProcessingState creationProcessingState) { - final SqmPathRegistry pathRegistry = creationProcessingState.getPathRegistry(); + final var pathRegistry = creationProcessingState.getPathRegistry(); return new SqmEntityJoin<>( getReferencedPathSource(), getExplicitAlias(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFunctionJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFunctionJoin.java index 660d80a6184e..e4f05e647b3f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFunctionJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFunctionJoin.java @@ -97,12 +97,12 @@ public boolean isImplicitlySelectable() { @Override public SqmFunctionJoin copy(SqmCopyContext context) { - final SqmFunctionJoin existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } //noinspection unchecked - final SqmFunctionJoin path = context.registerCopy( + final var path = context.registerCopy( this, new SqmFunctionJoin<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmRoot.java index 3b396a94e1a9..7050dd7cbfd4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmRoot.java @@ -73,11 +73,11 @@ protected SqmRoot( @Override public SqmRoot copy(SqmCopyContext context) { - final SqmRoot existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } - final SqmRoot path = context.registerCopy( + final var path = context.registerCopy( this, new SqmRoot<>( getNavigablePath(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java index be4e281908e8..fc68a466ecd6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java @@ -148,7 +148,7 @@ public NodeBuilder nodeBuilder() { @Override public SqmConflictClause copy(SqmCopyContext context) { - final SqmConflictClause existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictUpdateAction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictUpdateAction.java index 060eef3d369d..da5ecf4b0076 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictUpdateAction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictUpdateAction.java @@ -136,7 +136,7 @@ public NodeBuilder nodeBuilder() { @Override public SqmConflictUpdateAction copy(SqmCopyContext context) { - final SqmConflictUpdateAction existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java index 97177adfae92..e2c8cbbb7ed1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java @@ -75,7 +75,7 @@ private SqmInsertSelectStatement( @Override public SqmInsertSelectStatement copy(SqmCopyContext context) { - final SqmInsertSelectStatement existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java index 8f9198e689d0..4acd969d8c88 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java @@ -74,7 +74,7 @@ private SqmInsertValuesStatement( @Override public SqmInsertValuesStatement copy(SqmCopyContext context) { - final SqmInsertValuesStatement existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java index be145463d679..65123e6ee5e5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInListPredicate.java @@ -67,7 +67,7 @@ public SqmInListPredicate( @Override public SqmInListPredicate copy(SqmCopyContext context) { - final SqmInListPredicate existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInSubQueryPredicate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInSubQueryPredicate.java index 3cb9064939f4..ca73b4928d5a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInSubQueryPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/predicate/SqmInSubQueryPredicate.java @@ -57,7 +57,7 @@ public SqmInSubQueryPredicate( @Override public SqmInSubQueryPredicate copy(SqmCopyContext context) { - final SqmInSubQueryPredicate existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java index 12cf9b69e5ce..13b1675ae760 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java @@ -194,7 +194,7 @@ public boolean isFullyAliased() { @Override public SqmDynamicInstantiation copy(SqmCopyContext context) { - final SqmDynamicInstantiation existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java index e4d6fedc05f7..548a55431c5c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmJpaCompoundSelection.java @@ -60,7 +60,7 @@ public SqmJpaCompoundSelection( @Override public SqmJpaCompoundSelection copy(SqmCopyContext context) { - final SqmJpaCompoundSelection existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQueryGroup.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQueryGroup.java index bd61a0088b3b..f5017e192dbd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQueryGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQueryGroup.java @@ -62,7 +62,7 @@ public SqmQueryGroup( @Override public SqmQueryPart copy(SqmCopyContext context) { - final SqmQueryGroup existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java index 325432fe867c..3533d8e13524 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java @@ -98,7 +98,7 @@ public SqmQuerySpec(SqmQuerySpec original, SqmCopyContext context) { @Override public SqmQuerySpec copy(SqmCopyContext context) { - final SqmQuerySpec existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectStatement.java index 4de3b90080da..18782b6f52cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectStatement.java @@ -122,7 +122,7 @@ private SqmSelectStatement( @Override public SqmSelectStatement copy(SqmCopyContext context) { - final SqmSelectStatement existing = context.getCopy( this ); + final var existing = context.getCopy( this ); return existing != null ? existing : createCopy( context, getResultType() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSubQuery.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSubQuery.java index 1d086f3bce81..b0bc52227bea 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSubQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSubQuery.java @@ -150,7 +150,7 @@ private SqmSubQuery( @Override public SqmSubQuery copy(SqmCopyContext context) { - final SqmSubQuery existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java index 5e9b8e7905a0..981226432c4a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java @@ -107,7 +107,7 @@ private SqmUpdateStatement( @Override public SqmUpdateStatement copy(SqmCopyContext context) { - final SqmUpdateStatement existing = context.getCopy( this ); + final var existing = context.getCopy( this ); if ( existing != null ) { return existing; } @@ -179,7 +179,7 @@ public void setSetClause(SqmSetClause setClause) { @Override public SqmUpdateStatement set(SingularAttribute attribute, @Nullable X value) { - final SqmCriteriaNodeBuilder nodeBuilder = (SqmCriteriaNodeBuilder) nodeBuilder(); + final var nodeBuilder = (SqmCriteriaNodeBuilder) nodeBuilder(); SqmPath sqmAttribute = getTarget().get( attribute ); applyAssignment( sqmAttribute, nodeBuilder.value( value, sqmAttribute) ); return this; @@ -193,7 +193,7 @@ public SqmUpdateStatement set(SingularAttribute attribute, @Override public SqmUpdateStatement set(Path attribute, @Nullable X value) { - final SqmCriteriaNodeBuilder nodeBuilder = (SqmCriteriaNodeBuilder) nodeBuilder(); + final var nodeBuilder = (SqmCriteriaNodeBuilder) nodeBuilder(); final SqmPath sqmAttribute = (SqmPath) attribute; applyAssignment( sqmAttribute, nodeBuilder.value( value, sqmAttribute ) ); return this; @@ -213,7 +213,7 @@ public SqmUpdateStatement set(String attributeName, @Nullable Object value) { expression = (SqmExpression) value; } else { - final SqmCriteriaNodeBuilder nodeBuilder = (SqmCriteriaNodeBuilder) nodeBuilder(); + final var nodeBuilder = (SqmCriteriaNodeBuilder) nodeBuilder(); expression = nodeBuilder.value( value, sqmPath ); } applyAssignment( sqmPath, expression ); From 6bb96fca00bdca81733ada7c4a59a2ba6d6fff35 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 7 Dec 2025 18:58:34 +0100 Subject: [PATCH 049/181] fixed the unsoundness of the coerce() method --- .../internal/DefaultCacheKeysFactory.java | 8 +-- .../cache/internal/EnabledCaching.java | 6 +- .../internal/AbstractMultiIdEntityLoader.java | 8 +-- .../loader/ast/internal/LoaderHelper.java | 2 +- .../internal/IdentifierLoadAccessImpl.java | 11 +-- .../internal/SimpleNaturalIdMapping.java | 12 +--- .../internal/QueryParameterBindingImpl.java | 5 +- .../sqm/sql/BaseSqmToSqlAstConverter.java | 3 +- .../graph/basic/CoercingResultAssembler.java | 5 +- .../type/AbstractStandardBasicType.java | 2 +- .../type/descriptor/java/ArrayJavaType.java | 6 +- .../descriptor/java/BigDecimalJavaType.java | 2 +- .../descriptor/java/BigIntegerJavaType.java | 2 +- .../type/descriptor/java/ByteJavaType.java | 2 +- .../type/descriptor/java/DoubleJavaType.java | 2 +- .../type/descriptor/java/FloatJavaType.java | 2 +- .../type/descriptor/java/IntegerJavaType.java | 2 +- .../type/descriptor/java/JavaType.java | 18 ++--- .../descriptor/java/JdbcDateJavaType.java | 19 +++--- .../descriptor/java/JdbcTimeJavaType.java | 25 ++++--- .../java/JdbcTimestampJavaType.java | 29 ++++---- .../type/descriptor/java/LongJavaType.java | 2 +- .../java/PrimitiveByteArrayJavaType.java | 7 +- .../java/PrimitiveCharacterArrayJavaType.java | 2 +- .../type/descriptor/java/ShortJavaType.java | 2 +- .../type/descriptor/java/StringJavaType.java | 2 +- .../jdbc/spi/BasicJdbcLiteralFormatter.java | 2 +- .../test/mapping/type/java/CoercionTests.java | 68 +++++++++---------- 28 files changed, 113 insertions(+), 143 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/DefaultCacheKeysFactory.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/DefaultCacheKeysFactory.java index e9710a217c42..6348e4737e5d 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/DefaultCacheKeysFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/DefaultCacheKeysFactory.java @@ -45,7 +45,7 @@ public class DefaultCacheKeysFactory implements CacheKeysFactory { public static Object staticCreateCollectionKey(Object id, CollectionPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) { final Type keyType = persister.getKeyType(); - final var coercedId = getCoercedId( id, factory, keyType ); + final var coercedId = getCoercedId( id, keyType ); final var disassembledKey = keyType.disassemble( coercedId, factory ); final boolean idIsArray = disassembledKey.getClass().isArray(); final String role = persister.getRole(); @@ -56,7 +56,7 @@ public static Object staticCreateCollectionKey(Object id, CollectionPersister pe public static Object staticCreateEntityKey(Object id, EntityPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) { final Type keyType = persister.getIdentifierType(); - final var coercedId = getCoercedId( id, factory, keyType ); + final var coercedId = getCoercedId( id, keyType ); final var disassembledKey = keyType.disassemble( coercedId, factory ); final boolean idIsArray = disassembledKey.getClass().isArray(); final String rootEntityName = persister.getRootEntityName(); @@ -65,9 +65,9 @@ public static Object staticCreateEntityKey(Object id, EntityPersister persister, : new CacheKeyImplementation( coercedId, disassembledKey, keyType, rootEntityName, tenantIdentifier ); } - private static Object getCoercedId(Object id, SessionFactoryImplementor factory, Type keyType) { + private static Object getCoercedId(Object id, Type keyType) { return keyType instanceof BasicType basicType - ? basicType.getJavaTypeDescriptor().coerce( id, factory::getTypeConfiguration ) + ? basicType.getJavaTypeDescriptor().coerce( id ) : id; } diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java index 76c233fc60cb..06dc1ebf52bc 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java @@ -227,11 +227,9 @@ public boolean containsEntity(String entityName, Object identifier) { final var persister = getEntityDescriptor( entityName ); final var cacheAccess = persister.getCacheAccessStrategy(); if ( cacheAccess != null ) { - final Object idValue = - persister.getIdentifierMapping().getJavaType() - .coerce( identifier, sessionFactory::getTypeConfiguration ); final Object key = cacheAccess.generateCacheKey( - idValue, + persister.getIdentifierMapping().getJavaType() + .coerce( identifier ), persister.getRootEntityDescriptor().getEntityPersister(), sessionFactory, null diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java index a792b296e9cb..1279324f9be7 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java @@ -130,7 +130,7 @@ private List orderedMultiLoad( final var lockOptions = lockOptions( loadOptions ); for ( int i = 0; i < ids.length; i++ ) { - final Object id = coerce( session, idType, ids[i] ); + final Object id = coerce( idType, ids[i] ); final var entityKey = new EntityKey( id, getLoadable().getEntityPersister() ); if ( !loadFromEnabledCaches( loadOptions, session, id, lockOptions, entityKey, results, i ) ) { // if we did not hit any of the continues above, @@ -160,8 +160,8 @@ private List orderedMultiLoad( return (List) results; } - private Object coerce(SharedSessionContractImplementor session, JavaType idType, Object id) { - return idCoercionEnabled ? idType.coerce( id, session ) : id; + private Object coerce(JavaType idType, Object id) { + return idCoercionEnabled ? idType.coerce( id ) : id; } private static LockOptions lockOptions(MultiIdLoadOptions loadOptions) { @@ -342,7 +342,7 @@ private List unresolvedIds( final var idType = getLoadable().getIdentifierMapping().getJavaType(); List unresolvedIds = null; for ( int i = 0; i < ids.length; i++ ) { - final Object id = coerce( session, idType, ids[i] ); + final Object id = coerce( idType, ids[i] ); unresolvedIds = loadFromCaches( loadOptions, diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java index 77fc6ac9a14a..b79661bbf050 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java @@ -188,7 +188,7 @@ public static K[] normalizeKeys( } else { for ( int i = 0; i < keys.length; i++ ) { - typedArray[i] = keyJavaType.coerce( keys[i], session ); + typedArray[i] = keyJavaType.cast( keyJavaType.coerce( keys[i] ) ); } } return typedArray; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/IdentifierLoadAccessImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/IdentifierLoadAccessImpl.java index d947ad688912..6bb5a3423a2a 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/IdentifierLoadAccessImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/IdentifierLoadAccessImpl.java @@ -26,8 +26,6 @@ import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.type.descriptor.java.JavaType; -import org.hibernate.type.spi.TypeConfiguration; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; @@ -37,7 +35,7 @@ * @author Steve Ebersole */ // Hibernate Reactive extends this class: see ReactiveIdentifierLoadAccessImpl -public class IdentifierLoadAccessImpl implements IdentifierLoadAccess, JavaType.CoercionContext { +public class IdentifierLoadAccessImpl implements IdentifierLoadAccess { private final LoadAccessContext context; private final EntityPersister entityPersister; @@ -199,7 +197,7 @@ protected Object coerceId(Object id, SessionFactoryImplementor factory) { final var identifierMapping = entityPersister.getIdentifierMapping(); return identifierMapping.isVirtual() ? id // special case for a class with an @IdClass - : identifierMapping.getJavaType().coerce( id, this ); + : identifierMapping.getJavaType().coerce( id ); } catch ( Exception e ) { throw new IllegalArgumentException( "Argument '" + id @@ -233,11 +231,6 @@ private static boolean isLoadByIdComplianceEnabled(SessionFactoryImplementor fac return factory.getSessionFactoryOptions().getJpaCompliance().isLoadByIdComplianceEnabled(); } - @Override - public TypeConfiguration getTypeConfiguration() { - return context.getSession().getSessionFactory().getTypeConfiguration(); - } - @Override public IdentifierLoadAccess enableFetchProfile(String profileName) { if ( !context.getSession().getFactory().containsFetchProfileDefinition( profileName ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleNaturalIdMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleNaturalIdMapping.java index 555117bf9d97..f8910b60e35d 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleNaturalIdMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleNaturalIdMapping.java @@ -34,7 +34,6 @@ import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.type.descriptor.java.JavaType; -import org.hibernate.type.spi.TypeConfiguration; import static org.hibernate.loader.ast.internal.MultiKeyLoadHelper.supportsSqlArrayType; @@ -42,10 +41,9 @@ * Single-attribute NaturalIdMapping implementation */ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping - implements JavaType.CoercionContext, BasicValuedMapping { + implements BasicValuedMapping { private final SingularAttributeMapping attribute; private final SessionFactoryImplementor sessionFactory; - private final TypeConfiguration typeConfiguration; public SimpleNaturalIdMapping( SingularAttributeMapping attribute, @@ -54,7 +52,6 @@ public SimpleNaturalIdMapping( super( declaringType, attribute.getAttributeMetadata().isUpdatable() ); this.attribute = attribute; this.sessionFactory = creationProcess.getCreationContext().getSessionFactory(); - this.typeConfiguration = creationProcess.getCreationContext().getTypeConfiguration(); } public SingularAttributeMapping getAttribute() { @@ -143,7 +140,7 @@ public Object normalizeInput(Object incoming) { final Object normalizedValue = normalizedValue( incoming ); return isLoadByIdComplianceEnabled() ? normalizedValue - : getJavaType().coerce( normalizedValue, this ); + : getJavaType().coerce( normalizedValue ); } private Object normalizedValue(Object incoming) { @@ -295,11 +292,6 @@ private Dialect getDialect() { return sessionFactory.getJdbcServices().getDialect(); } - @Override - public TypeConfiguration getTypeConfiguration() { - return typeConfiguration; - } - @Override public AttributeMapping asAttributeMapping() { return getAttribute(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java index 1341b1fdd2ff..4c9bdd7d91b5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java @@ -31,7 +31,7 @@ * * @author Steve Ebersole */ -public class QueryParameterBindingImpl implements QueryParameterBinding, JavaType.CoercionContext { +public class QueryParameterBindingImpl implements QueryParameterBinding { private final QueryParameter queryParameter; private final SessionFactoryImplementor sessionFactory; @@ -296,7 +296,6 @@ private void validate(Object value, TemporalType clarifiedTemporalType) { QueryParameterBindingValidator.INSTANCE.validate( getBindType(), value, clarifiedTemporalType, getCriteriaBuilder() ); } - @Override public TypeConfiguration getTypeConfiguration() { return sessionFactory.getTypeConfiguration(); } @@ -337,7 +336,7 @@ private Object coerce(T value, BindableType parameterType) { } else { return getCriteriaBuilder().resolveExpressible( parameterType ) - .getExpressibleJavaType().coerce( value, this ); + .getExpressibleJavaType().coerce( value ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index c70b1b74d6b0..35ad112e177b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -5812,8 +5812,7 @@ else if ( Character.class.isAssignableFrom( valueConverter.getRelationalJavaType // so we allow coercion between the number types else if ( Number.class.isAssignableFrom( valueConverter.getRelationalJavaType().getJavaTypeClass() ) && value instanceof Number ) { - return valueConverter.getRelationalJavaType() - .coerce( value ); + return valueConverter.getRelationalJavaType().coerce( value ); } else { throw new SemanticException( diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/CoercingResultAssembler.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/CoercingResultAssembler.java index 91e9f003a081..6676eaad3fdd 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/CoercingResultAssembler.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/CoercingResultAssembler.java @@ -29,9 +29,6 @@ public CoercingResultAssembler( */ @Override public Object extractRawValue(RowProcessingState rowProcessingState) { - return assembledJavaType.coerce( - super.extractRawValue( rowProcessingState ), - rowProcessingState.getSession() - ); + return assembledJavaType.coerce( super.extractRawValue( rowProcessingState ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java b/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java index 4dca9e44c747..62a4ceed34ee 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java @@ -258,7 +258,7 @@ public final String toLoggableString(Object value, SessionFactoryImplementor fac || !Hibernate.isInitialized( value ) ? "" : javaType.extractLoggableRepresentation( - javaType.coerce( value, factory::getTypeConfiguration ) ); + javaType.cast( javaType.coerce( value ) ) ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java index 8010b895a3b4..8ecada319add 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java @@ -128,8 +128,8 @@ public boolean areEqual(Object[] one, Object[] another) { // Horrible hack around the fact that java.sql.Timestamps // can be represented as instances of java.util.Date // (Why do we even allow this? We deprecated java.sql stuff!) - elementJavaType.coerce( one[i], null ), - elementJavaType.coerce( another[i], null ) )) { + elementJavaType.cast( elementJavaType.coerce( one[i] ) ), + elementJavaType.cast( elementJavaType.coerce( another[i] ) ) ) ) { return false; } } @@ -448,7 +448,7 @@ public T[] deepCopy(Object[] value) { // Horrible hack around the fact that java.sql.Timestamps // can be represented as instances of java.util.Date // (Why do we even allow this? We deprecated java.sql stuff!) - baseDescriptor.coerce( value[i], null ) ); + baseDescriptor.cast( baseDescriptor.coerce( value[i] ) ) ); } return copy; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigDecimalJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigDecimalJavaType.java index c04e276c3de9..4d6c75c1e10e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigDecimalJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigDecimalJavaType.java @@ -130,7 +130,7 @@ public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { } @Override - public BigDecimal coerce(X value, CoercionContext coercionContext) { + public BigDecimal coerce(Object value) { if ( value == null ) { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigIntegerJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigIntegerJavaType.java index 02cbb5d930b1..5b4a44bd0563 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigIntegerJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BigIntegerJavaType.java @@ -132,7 +132,7 @@ public int getDefaultSqlScale(Dialect dialect, JdbcType jdbcType) { } @Override - public BigInteger coerce(X value, CoercionContext coercionContext) { + public BigInteger coerce(Object value) { if ( value == null ) { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteJavaType.java index ad7a4c5387f5..7e143c378b53 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteJavaType.java @@ -136,7 +136,7 @@ public int getDefaultSqlScale(Dialect dialect, JdbcType jdbcType) { } @Override - public Byte coerce(X value, CoercionContext coercionContext) { + public Byte coerce(Object value) { if ( value == null ) { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleJavaType.java index a4cfcd16d679..6ee06c11b55c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleJavaType.java @@ -164,7 +164,7 @@ public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { } @Override - public Double coerce(X value, CoercionContext coercionContext) { + public Double coerce(Object value) { if ( value == null ) { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatJavaType.java index 66ed69088b7d..458640f20522 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatJavaType.java @@ -162,7 +162,7 @@ public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { } @Override - public Float coerce(X value, CoercionContext coercionContext) { + public Float coerce(Object value) { if ( value == null ) { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerJavaType.java index a38d4827a3d1..e6e3364f5e8b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerJavaType.java @@ -152,7 +152,7 @@ public int getDefaultSqlScale(Dialect dialect, JdbcType jdbcType) { } @Override - public Integer coerce(Object value, CoercionContext coercionContext) { + public Integer coerce(Object value) { if ( value == null ) { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java index 06f7f626be40..93950012891a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java @@ -315,32 +315,22 @@ default boolean isWider(JavaType javaType) { } @FunctionalInterface + @Deprecated(forRemoval = true, since = "7.2") interface CoercionContext { TypeConfiguration getTypeConfiguration(); } /** * Coerce the given value to this type, if possible. - * The default implementation defined her simply - * performs an unchecked cast. Subclasses may override - * to perform meaningful coercion. - * - * @apiNote This operation is currently unsound. It - * should throw {@link CoercionException} when coercion - * fails, or its return value should be changed to - * {@link Object}. However, both of those changes had - * too much impact for now. * * @param value The value to coerce - * @param coercionContext The coercion context * @return The coerced value, or the given value * if no coercion was possible - * @param The type of the value + * @throws CoercionException if coercion fails */ @Incubating - default T coerce(X value, CoercionContext coercionContext) { - //noinspection unchecked - return (T) value; + default Object coerce(Object value) { + return value; } /** diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java index bdfb6c23e8ba..9258833af50a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java @@ -115,45 +115,44 @@ public int extractHashCode(Date value) { } @Override - public Date coerce(Object value, CoercionContext coercionContext) { + public Date coerce(Object value) { return wrap( value, null ); } - @SuppressWarnings({"unchecked", "rawtypes"}) @Override - public Object unwrap(Date value, Class type, WrapperOptions options) { + public X unwrap(Date value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( LocalDate.class.isAssignableFrom( type ) ) { - return unwrapLocalDate( value ); + return type.cast( unwrapLocalDate( value ) ); } if ( java.sql.Date.class.isAssignableFrom( type ) ) { - return unwrapSqlDate( value ); + return type.cast( unwrapSqlDate( value ) ); } if ( java.util.Date.class.isAssignableFrom( type ) ) { - return value; + return type.cast( value ); } if ( Long.class.isAssignableFrom( type ) ) { - return unwrapDateEpoch( value ); + return type.cast( unwrapDateEpoch( value ) ); } if ( String.class.isAssignableFrom( type ) ) { - return toString( value ); + return type.cast( toString( value ) ); } if ( Calendar.class.isAssignableFrom( type ) ) { final var gregorianCalendar = new GregorianCalendar(); gregorianCalendar.setTimeInMillis( unwrapDateEpoch( value ) ); - return gregorianCalendar; + return type.cast( gregorianCalendar ); } if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { - return new java.sql.Timestamp( unwrapDateEpoch( value ) ); + return type.cast( new java.sql.Timestamp( unwrapDateEpoch( value ) ) ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java index 48bc52f289e2..af0af15fe7f0 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java @@ -117,13 +117,12 @@ public boolean areEqual(Date one, Date another) { } @Override - public Date coerce(Object value, CoercionContext coercionContext) { + public Date coerce(Object value) { return wrap( value, null ); } - @SuppressWarnings({"unchecked", "rawtypes"}) @Override - public Object unwrap(Date value, Class type, WrapperOptions options) { + public X unwrap(Date value, Class type, WrapperOptions options) { if ( value == null ) { return null; } @@ -136,42 +135,42 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { final var localTime = time.toLocalTime(); long millis = time.getTime() % 1000; if ( millis == 0 ) { - return localTime; + return type.cast( localTime ); } if ( millis < 0 ) { // The milliseconds for a Time could be negative, // which usually means the time is in a different time zone millis += 1_000L; } - return localTime.with( ChronoField.NANO_OF_SECOND, millis * 1_000_000L ); + return type.cast( localTime.with( ChronoField.NANO_OF_SECOND, millis * 1_000_000L ) ); } if ( Time.class.isAssignableFrom( type ) ) { - return value instanceof Time - ? value - : new Time( value.getTime() % 86_400_000 ); + return type.cast( value instanceof Time time + ? time + : new Time( value.getTime() % 86_400_000 ) ); } if ( Date.class.isAssignableFrom( type ) ) { - return value; + return type.cast( value ); } if ( Long.class.isAssignableFrom( type ) ) { - return value.getTime(); + return type.cast( value.getTime() ); } if ( String.class.isAssignableFrom( type ) ) { - return toString( value ); + return type.cast( toString( value ) ); } if ( Calendar.class.isAssignableFrom( type ) ) { final var gregorianCalendar = new GregorianCalendar(); gregorianCalendar.setTimeInMillis( value.getTime() ); - return gregorianCalendar; + return type.cast( gregorianCalendar ); } if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { - return new java.sql.Timestamp( value.getTime() ); + return type.cast( new java.sql.Timestamp( value.getTime() ) ); } if ( java.sql.Date.class.isAssignableFrom( type ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java index 2b8f61a016e1..abd6a4079802 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java @@ -114,52 +114,51 @@ public int extractHashCode(Date value) { } @Override - public Date coerce(Object value, CoercionContext coercionContext) { + public Date coerce(Object value) { return wrap( value, null ); } - @SuppressWarnings({"unchecked", "rawtypes"}) @Override - public Object unwrap(Date value, Class type, WrapperOptions options) { + public X unwrap(Date value, Class type, WrapperOptions options) { if ( value == null ) { return null; } if ( Timestamp.class.isAssignableFrom( type ) ) { - return value instanceof Timestamp timestamp + return type.cast( value instanceof Timestamp timestamp ? timestamp - : new Timestamp( value.getTime() ); + : new Timestamp( value.getTime() ) ); } if ( Date.class.isAssignableFrom( type ) ) { - return value; + return type.cast( value ); } if ( LocalDateTime.class.isAssignableFrom( type ) ) { final var instant = value.toInstant(); - return LocalDateTime.ofInstant( instant, ZoneId.systemDefault() ); + return type.cast( LocalDateTime.ofInstant( instant, ZoneId.systemDefault() ) ); } if ( Calendar.class.isAssignableFrom( type ) ) { final var gregorianCalendar = new GregorianCalendar(); gregorianCalendar.setTimeInMillis( value.getTime() ); - return gregorianCalendar; + return type.cast( gregorianCalendar ); } if ( Long.class.isAssignableFrom( type ) ) { - return value.getTime(); + return type.cast( value.getTime() ); } if ( java.sql.Date.class.isAssignableFrom( type ) ) { - return value instanceof java.sql.Date - ? (java.sql.Date) value - : new java.sql.Date( value.getTime() ); + return type.cast( value instanceof java.sql.Date date + ? date + : new java.sql.Date( value.getTime() ) ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - return value instanceof java.sql.Time - ? (java.sql.Time) value - : new java.sql.Time( value.getTime() % 86_400_000 ); + return type.cast( value instanceof java.sql.Time time + ? time + : new java.sql.Time( value.getTime() % 86_400_000 ) ); } throw unknownUnwrap( type ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongJavaType.java index f8950f859f69..b7c6d57a2b73 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongJavaType.java @@ -118,7 +118,7 @@ public boolean isWider(JavaType javaType) { } @Override - public Long coerce(X value, CoercionContext coercionContext) { + public Long coerce(Object value) { if ( value == null ) { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java index f0b51879ce48..bfaedc9b68dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java @@ -164,10 +164,15 @@ else if ( value instanceof Byte[] array ) { throw unknownWrap( value.getClass() ); } + @Override + public byte[] coerce(Object value) { + return wrap( value, null ); + } + @Override public byte[] seed( Long length, Integer precision, Integer scale, SharedSessionContractImplementor session) { - // Note : simply returns null for seed() and next() as the only known + // Note: simply returns null for seed() and next() as the only known // application of binary types for versioning is for use with the // TIMESTAMP datatype supported by Sybase and SQL Server, which // are completely db-generated values... diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveCharacterArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveCharacterArrayJavaType.java index dad8386ce0dd..7bcb1fca2908 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveCharacterArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveCharacterArrayJavaType.java @@ -109,7 +109,7 @@ else if ( value instanceof Character character ) { } @Override - public char[] coerce(X value, CoercionContext coercionContext) { + public char[] coerce(Object value) { return wrap( value, null ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortJavaType.java index ed6ef2f81e7d..ad03c859e188 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortJavaType.java @@ -143,7 +143,7 @@ public int getDefaultSqlScale(Dialect dialect, JdbcType jdbcType) { } @Override - public Short coerce(Object value, CoercionContext coercionContext) { + public Short coerce(Object value) { if ( value == null ) { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/StringJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/StringJavaType.java index 55bcd5c45b87..a10b9fc29ac4 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/StringJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/StringJavaType.java @@ -147,7 +147,7 @@ public boolean isWider(JavaType javaType) { } @Override - public String coerce(X value, CoercionContext coercionContext) { + public String coerce(Object value) { return wrap( value, null ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/spi/BasicJdbcLiteralFormatter.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/spi/BasicJdbcLiteralFormatter.java index c1677f9078fb..0048cbca97da 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/spi/BasicJdbcLiteralFormatter.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/spi/BasicJdbcLiteralFormatter.java @@ -32,7 +32,7 @@ protected X unwrap(Object value, Class unwrapType, WrapperOptions options return javaType.unwrap( castValue, unwrapType, options ); } else { - final T coerced = javaType.coerce( value, options::getTypeConfiguration ); + final T coerced = javaType.cast( javaType.coerce( value ) ); return unwrapType.isInstance( coerced ) ? unwrapType.cast( coerced ) : javaType.unwrap( coerced, unwrapType, options ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/CoercionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/CoercionTests.java index 7f007c7b2c84..ac353c2bf2fb 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/CoercionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/CoercionTests.java @@ -70,62 +70,62 @@ public void testCoercibleDetection(SessionFactoryScope scope) { } private void checkDoubleConversions(JavaType doubleType, SessionImplementor session) { - assertThat( doubleType.coerce( (double) 1, session ) ).isEqualTo( 1.0 ); - assertThat( doubleType.coerce( 1F, session ) ).isEqualTo( 1.0 ); - assertThat( doubleType.coerce( doubleValue, session ) ).isEqualTo( doubleValue ); - assertThat( doubleType.coerce( floatValue, session ) ).isEqualTo( doubleValue ); + assertThat( doubleType.coerce( (double) 1 ) ).isEqualTo( 1.0 ); + assertThat( doubleType.coerce( 1F ) ).isEqualTo( 1.0 ); + assertThat( doubleType.coerce( doubleValue ) ).isEqualTo( doubleValue ); + assertThat( doubleType.coerce( floatValue ) ).isEqualTo( doubleValue ); - assertThat( doubleType.coerce( largeFloatValue, session ) ).isEqualTo( largeFloatValue ); + assertThat( doubleType.coerce( largeFloatValue ) ).isEqualTo( Double.valueOf( largeFloatValue ) ); - assertThat( doubleType.coerce( shortValue, session ) ).isEqualTo( 1.0 ); - assertThat( doubleType.coerce( byteValue, session ) ).isEqualTo( 1.0 ); - assertThat( doubleType.coerce( longValue, session ) ).isEqualTo( 1.0 ); + assertThat( doubleType.coerce( shortValue ) ).isEqualTo( 1.0 ); + assertThat( doubleType.coerce( byteValue ) ).isEqualTo( 1.0 ); + assertThat( doubleType.coerce( longValue ) ).isEqualTo( 1.0 ); - assertThat( doubleType.coerce( BigInteger.ONE, session ) ).isEqualTo( 1.0 ); - assertThat( doubleType.coerce( BigDecimal.ONE, session ) ).isEqualTo( 1.0 ); + assertThat( doubleType.coerce( BigInteger.ONE ) ).isEqualTo( 1.0 ); + assertThat( doubleType.coerce( BigDecimal.ONE ) ).isEqualTo( 1.0 ); // negative checks } private void checkIntegerConversions(JavaType integerType, SessionImplementor session) { - assertThat( integerType.coerce( intValue, session ) ).isEqualTo( intValue ); + assertThat( integerType.coerce( intValue ) ).isEqualTo( intValue ); - assertThat( integerType.coerce( shortValue, session ) ).isEqualTo( intValue ); - assertThat( integerType.coerce( byteValue, session ) ).isEqualTo( intValue ); + assertThat( integerType.coerce( shortValue ) ).isEqualTo( intValue ); + assertThat( integerType.coerce( byteValue ) ).isEqualTo( intValue ); - assertThat( integerType.coerce( longValue, session ) ).isEqualTo( intValue ); + assertThat( integerType.coerce( longValue ) ).isEqualTo( intValue ); - assertThat( integerType.coerce( (double) 1, session ) ).isEqualTo( intValue ); - assertThat( integerType.coerce( 1F, session ) ).isEqualTo( intValue ); + assertThat( integerType.coerce( (double) 1 ) ).isEqualTo( intValue ); + assertThat( integerType.coerce( 1F ) ).isEqualTo( intValue ); - assertThat( integerType.coerce( BigInteger.ONE, session ) ).isEqualTo( intValue ); - assertThat( integerType.coerce( BigDecimal.ONE, session ) ).isEqualTo( intValue ); + assertThat( integerType.coerce( BigInteger.ONE ) ).isEqualTo( intValue ); + assertThat( integerType.coerce( BigDecimal.ONE ) ).isEqualTo( intValue ); // negative checks - checkDisallowedConversion( () -> integerType.coerce( largeLongValue, session ) ); - checkDisallowedConversion( () -> integerType.coerce( largeFloatValue, session ) ); - checkDisallowedConversion( () -> integerType.coerce( doubleValue, session ) ); - checkDisallowedConversion( () -> integerType.coerce( floatValue, session ) ); + checkDisallowedConversion( () -> integerType.coerce( largeLongValue ) ); + checkDisallowedConversion( () -> integerType.coerce( largeFloatValue ) ); + checkDisallowedConversion( () -> integerType.coerce( doubleValue ) ); + checkDisallowedConversion( () -> integerType.coerce( floatValue ) ); } private void checkLongConversions(JavaType longType, SessionImplementor session) { - assertThat( longType.coerce( longValue, session ) ).isEqualTo( longValue ); - assertThat( longType.coerce( largeLongValue, session ) ).isEqualTo( largeLongValue ); + assertThat( longType.coerce( longValue ) ).isEqualTo( longValue ); + assertThat( longType.coerce( largeLongValue ) ).isEqualTo( largeLongValue ); - assertThat( longType.coerce( intValue, session ) ).isEqualTo( longValue ); - assertThat( longType.coerce( shortValue, session ) ).isEqualTo( longValue ); - assertThat( longType.coerce( byteValue, session ) ).isEqualTo( longValue ); + assertThat( longType.coerce( intValue ) ).isEqualTo( longValue ); + assertThat( longType.coerce( shortValue ) ).isEqualTo( longValue ); + assertThat( longType.coerce( byteValue ) ).isEqualTo( longValue ); - assertThat( longType.coerce( (double) 1, session ) ).isEqualTo( longValue ); - assertThat( longType.coerce( 1F, session ) ).isEqualTo( longValue ); + assertThat( longType.coerce( (double) 1 ) ).isEqualTo( longValue ); + assertThat( longType.coerce( 1F ) ).isEqualTo( longValue ); - assertThat( longType.coerce( BigInteger.ONE, session ) ).isEqualTo( longValue ); - assertThat( longType.coerce( BigDecimal.ONE, session ) ).isEqualTo( longValue ); + assertThat( longType.coerce( BigInteger.ONE ) ).isEqualTo( longValue ); + assertThat( longType.coerce( BigDecimal.ONE ) ).isEqualTo( longValue ); // negative checks - checkDisallowedConversion( () -> longType.coerce( largeFloatValue, session ) ); - checkDisallowedConversion( () -> longType.coerce( doubleValue, session ) ); - checkDisallowedConversion( () -> longType.coerce( floatValue, session ) ); + checkDisallowedConversion( () -> longType.coerce( largeFloatValue ) ); + checkDisallowedConversion( () -> longType.coerce( doubleValue ) ); + checkDisallowedConversion( () -> longType.coerce( floatValue ) ); } private void checkDisallowedConversion(CoercionHelper.Coercer callback) { From 9e40f1c8018379ca360202c6b19488b7031acbf2 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 8 Dec 2025 00:37:56 +0100 Subject: [PATCH 050/181] clean up obsolete code in EnabledCaching --- .../cache/internal/EnabledCaching.java | 52 +++++-------------- 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java index 06dc1ebf52bc..c927b2d92702 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/EnabledCaching.java @@ -4,9 +4,6 @@ */ package org.hibernate.cache.internal; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashSet; import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -71,9 +68,6 @@ public class EnabledCaching implements CacheImplementor, DomainDataRegionBuildin private final Map namedQueryResultsCacheMap = new ConcurrentHashMap<>(); - private final Set legacySecondLevelCacheNames = new LinkedHashSet<>(); - private final Map> legacyNaturalIdAccessesForRegion = new ConcurrentHashMap<>(); - public EnabledCaching(SessionFactoryImplementor sessionFactory) { this.sessionFactory = sessionFactory; final var options = sessionFactory.getSessionFactoryOptions(); @@ -105,7 +99,6 @@ private TimestampsCache buildTimestampsCache(SessionFactoryImplementor sessionFa DEFAULT_UPDATE_TIMESTAMPS_REGION_UNQUALIFIED_NAME, sessionFactory ); - legacySecondLevelCacheNames.add( timestampsRegion.getName() ); return sessionFactory.getSessionFactoryOptions().getTimestampsCacheFactory() .buildTimestampsCache( this, timestampsRegion ); } @@ -134,27 +127,16 @@ public void prime(Set cacheRegionConfigs) { for ( var entityAccessConfig : regionConfig.getEntityCaching() ) { final var navigableRole = entityAccessConfig.getNavigableRole(); entityAccessMap.put( navigableRole, region.getEntityDataAccess( navigableRole ) ); - legacySecondLevelCacheNames.add( qualifiedRegionName( region ) ); } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Natural-id caching - final var naturalIdCaching = regionConfig.getNaturalIdCaching(); - if ( naturalIdCaching.isEmpty() ) { - legacyNaturalIdAccessesForRegion.put( region.getName(), Collections.emptySet() ); - } - else { - final Set accesses = new HashSet<>(); - for ( var naturalIdAccessConfig : naturalIdCaching ) { - final var navigableRole = naturalIdAccessConfig.getNavigableRole(); - final var naturalIdDataAccess = - naturalIdAccessMap.put( navigableRole, - region.getNaturalIdDataAccess( navigableRole ) ); - accesses.add( naturalIdDataAccess ); - } - legacyNaturalIdAccessesForRegion.put( region.getName(), accesses ); + for ( var naturalIdAccessConfig : regionConfig.getNaturalIdCaching() ) { + final var navigableRole = naturalIdAccessConfig.getNavigableRole(); + naturalIdAccessMap.put( navigableRole, + region.getNaturalIdDataAccess( navigableRole ) ); } @@ -165,7 +147,6 @@ public void prime(Set cacheRegionConfigs) { final var navigableRole = collectionAccessConfig.getNavigableRole(); collectionAccessMap.put( navigableRole, region.getCollectionDataAccess( navigableRole ) ); - legacySecondLevelCacheNames.add( qualifiedRegionName( region ) ); } } @@ -213,7 +194,6 @@ public Region getRegion(String regionName) { } - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Entity data @@ -309,7 +289,6 @@ public void evictEntityData() { } - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Natural-id data @@ -341,7 +320,6 @@ private void evictNaturalIdData(NavigableRole rootEntityRole, NaturalIdDataAcces } - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Collection data @@ -427,7 +405,7 @@ private void evictQueryResultRegion(QueryResultsCache cache) { public void evictQueryRegions() { LOG.evictingAllQueryRegions(); evictQueryResultRegion( defaultQueryResultsCache ); - for ( QueryResultsCache cache : namedQueryResultsCacheMap.values() ) { + for ( var cache : namedQueryResultsCacheMap.values() ) { evictQueryResultRegion( cache ); } } @@ -470,7 +448,7 @@ else if ( regionName == null || regionName.equals( getDefaultResultCacheName() ) } else { final var existing = namedQueryResultsCacheMap.get( regionName ); - return existing != null ? existing : makeQueryResultsRegionAccess( regionName ); + return existing != null ? existing : makeQueryResultsCache( regionName ); } } @@ -487,21 +465,20 @@ else if ( regionName == null || regionName.equals( getDefaultResultCacheName() ) } } - protected QueryResultsCache makeQueryResultsRegionAccess(String regionName) { - final var regionAccess = new QueryResultsCacheImpl( getQueryResultsRegion( regionName ), timestampsCache ); + protected QueryResultsCache makeQueryResultsCache(String regionName) { + final var regionAccess = new QueryResultsCacheImpl( queryResultsRegion( regionName ), timestampsCache ); namedQueryResultsCacheMap.put( regionName, regionAccess ); - legacySecondLevelCacheNames.add( regionName ); return regionAccess; } - private QueryResultsRegion getQueryResultsRegion(String regionName) { - final Region region = regionsByName.computeIfAbsent( regionName, this::makeQueryResultsRegion ); - return region instanceof QueryResultsRegion queryResultsRegion + private QueryResultsRegion queryResultsRegion(String regionName) { + return regionsByName.computeIfAbsent( regionName, this::buildQueryResultsRegion ) + instanceof QueryResultsRegion queryResultsRegion ? queryResultsRegion // There was already a different type of Region with the same name. - : queryResultsRegionsByDuplicateName.computeIfAbsent( regionName, this::makeQueryResultsRegion ); + : queryResultsRegionsByDuplicateName.computeIfAbsent( regionName, this::buildQueryResultsRegion ); } - protected QueryResultsRegion makeQueryResultsRegion(String regionName) { + protected QueryResultsRegion buildQueryResultsRegion(String regionName) { return regionFactory.buildQueryResultsRegion( regionName, getSessionFactory() ); } @@ -544,7 +521,6 @@ public void close() { } - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // JPA-defined methods @@ -567,8 +543,6 @@ public void evict(Class cls) { } - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Deprecations From 433d83b8f31774ca209e6c3f111d260e119452f4 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 8 Dec 2025 00:36:26 +0100 Subject: [PATCH 051/181] try to improve type safety in JavaTypeRegistry and MutableMutabilityPlant --- .../convert/internal/ConverterHelper.java | 10 +- .../process/internal/VersionResolution.java | 21 +- .../dialect/function/array/DdlTypeHelper.java | 46 +-- ...yViaElementArgumentReturnTypeResolver.java | 15 +- .../util/type/PrimitiveWrapperHelper.java | 278 ------------------ .../internal/util/type/PrimitiveWrappers.java | 47 +++ .../domain/internal/MappingMetamodelImpl.java | 56 ++-- .../internal/ProjectionSpecificationImpl.java | 2 +- .../query/sql/internal/NativeQueryImpl.java | 8 +- .../query/sqm/internal/TypecheckUtil.java | 15 +- .../sqm/sql/BaseSqmToSqlAstConverter.java | 27 +- .../sql/results/internal/TupleImpl.java | 4 +- .../type/AbstractStandardBasicType.java | 7 +- .../org/hibernate/type/BasicTypeRegistry.java | 7 +- .../internal/AttributeConverterBean.java | 8 +- .../java/MutableMutabilityPlan.java | 7 +- .../descriptor/java/spi/JavaTypeRegistry.java | 158 +++++++--- .../descriptor/java/spi/RegistryHelper.java | 50 ++-- .../type/internal/ParameterizedTypeImpl.java | 76 ++--- .../hibernate/type/spi/TypeConfiguration.java | 117 +++++--- 20 files changed, 387 insertions(+), 572 deletions(-) delete mode 100644 hibernate-core/src/main/java/org/hibernate/internal/util/type/PrimitiveWrapperHelper.java create mode 100644 hibernate-core/src/main/java/org/hibernate/internal/util/type/PrimitiveWrappers.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/ConverterHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/ConverterHelper.java index d56ce6bfca52..324b813bd1ee 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/ConverterHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/ConverterHelper.java @@ -15,7 +15,6 @@ import org.hibernate.boot.spi.ClassmateContext; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.internal.util.GenericsHelper; -import org.hibernate.internal.util.type.PrimitiveWrapperHelper; import org.hibernate.models.spi.MemberDetails; import com.fasterxml.classmate.ResolvedType; @@ -24,6 +23,8 @@ import com.fasterxml.classmate.members.ResolvedMethod; import jakarta.persistence.AttributeConverter; +import static org.hibernate.internal.util.type.PrimitiveWrappers.canonicalize; + /** * Helpers related to handling converters */ @@ -100,11 +101,8 @@ else if ( converterParamTypes.size() != 2 ) { * @return {@code true} if they match, otherwise {@code false}. */ public static boolean typesMatch(ResolvedType converterDefinedType, ResolvedType checkType) { - Class erasedCheckType = checkType.getErasedType(); - if ( erasedCheckType.isPrimitive() ) { - erasedCheckType = PrimitiveWrapperHelper.getDescriptorByPrimitiveType( erasedCheckType ).getWrapperClass(); - } - else if ( erasedCheckType.isArray() ) { + final var erasedCheckType = canonicalize( checkType.getErasedType() ); + if ( erasedCheckType.isArray() ) { // converterDefinedType have type parameters if it extends super generic class // but checkType doesn't have any type parameters // comparing erased type is enough diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/VersionResolution.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/VersionResolution.java index d4250858d261..66cd05b562d9 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/VersionResolution.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/VersionResolution.java @@ -28,21 +28,21 @@ */ public class VersionResolution implements BasicValue.Resolution { - // todo (6.0) : support explicit JTD? - // todo (6.0) : support explicit STD? - - public static VersionResolution from( + public static VersionResolution from( Function implicitJavaTypeAccess, TimeZoneStorageType timeZoneStorageType, MetadataBuildingContext context) { - - // todo (6.0) : add support for Dialect-specific interpretation? - final var typeConfiguration = context.getBootstrapContext().getTypeConfiguration(); final var implicitJavaType = implicitJavaTypeAccess.apply( typeConfiguration ); - final JavaType registered = typeConfiguration.getJavaTypeRegistry().getDescriptor( implicitJavaType ); - final var basicJavaType = (BasicJavaType) registered; + final var registered = typeConfiguration.getJavaTypeRegistry().resolveDescriptor( implicitJavaType ); + return resolve( timeZoneStorageType, context, (BasicJavaType) registered ); + } + private static VersionResolution resolve( + TimeZoneStorageType timeZoneStorageType, + MetadataBuildingContext context, + BasicJavaType basicJavaType) { + final var typeConfiguration = context.getBootstrapContext().getTypeConfiguration(); final var recommendedJdbcType = basicJavaType.getRecommendedJdbcType( new JdbcTypeIndicators() { @Override @@ -50,7 +50,7 @@ public TypeConfiguration getTypeConfiguration() { return typeConfiguration; } - @Override + @Override @SuppressWarnings("deprecation") public TemporalType getTemporalPrecision() { // if it is a temporal version, it needs to be a TIMESTAMP return TemporalType.TIMESTAMP; @@ -106,7 +106,6 @@ public Dialect getDialect() { final var basicTypeRegistry = typeConfiguration.getBasicTypeRegistry(); final var basicType = basicTypeRegistry.resolve( basicJavaType, recommendedJdbcType ); final var legacyType = basicTypeRegistry.getRegisteredType( basicJavaType.getJavaTypeClass() ); - assert legacyType.getJdbcType().getDefaultSqlTypeCode() == recommendedJdbcType.getDefaultSqlTypeCode(); return new VersionResolution<>( basicJavaType, recommendedJdbcType, basicType, legacyType ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/DdlTypeHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/DdlTypeHelper.java index 1e5be6ab186c..c8e9be79381c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/DdlTypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/DdlTypeHelper.java @@ -7,9 +7,7 @@ import java.lang.reflect.Type; import java.util.List; -import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.Size; -import org.hibernate.internal.build.AllowReflection; import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.SqlTypedMapping; import org.hibernate.metamodel.model.domain.DomainType; @@ -17,24 +15,20 @@ import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.java.BasicPluralJavaType; -import org.hibernate.type.descriptor.sql.DdlType; -import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.internal.ParameterizedTypeImpl; import org.hibernate.type.spi.TypeConfiguration; public class DdlTypeHelper { @SuppressWarnings("unchecked") - @AllowReflection +// @AllowReflection public static BasicType resolveArrayType(DomainType elementType, TypeConfiguration typeConfiguration) { - @SuppressWarnings("unchecked") final var arrayJavaType = (BasicPluralJavaType) typeConfiguration.getJavaTypeRegistry() .resolveArrayDescriptor( elementType.getJavaType() ); - final Dialect dialect = typeConfiguration.getCurrentBaseSqlTypeIndicators().getDialect(); return arrayJavaType.resolveType( typeConfiguration, - dialect, + typeConfiguration.getCurrentBaseSqlTypeIndicators().getDialect(), (BasicType) elementType, null, typeConfiguration.getCurrentBaseSqlTypeIndicators() @@ -43,7 +37,6 @@ public static BasicType resolveArrayType(DomainType elementType, TypeConfi @SuppressWarnings("unchecked") public static BasicType resolveListType(DomainType elementType, TypeConfiguration typeConfiguration) { - @SuppressWarnings("unchecked") final BasicPluralJavaType arrayJavaType = (BasicPluralJavaType) typeConfiguration.getJavaTypeRegistry() @@ -52,10 +45,9 @@ public static BasicType resolveListType(DomainType elementType, TypeConfig new ParameterizedTypeImpl( List.class, new Type[]{ elementType.getJavaType() }, null ), typeConfiguration ); - final Dialect dialect = typeConfiguration.getCurrentBaseSqlTypeIndicators().getDialect(); return arrayJavaType.resolveType( typeConfiguration, - dialect, + typeConfiguration.getCurrentBaseSqlTypeIndicators().getDialect(), (BasicType) elementType, null, typeConfiguration.getCurrentBaseSqlTypeIndicators() @@ -79,11 +71,9 @@ public static String getTypeName(JdbcMappingContainer type, Size size, TypeConfi return AbstractSqlAstTranslator.getSqlTypeName( sqlTypedMapping, typeConfiguration ); } else { - final BasicType basicType = (BasicType) type.getSingleJdbcMapping(); - final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry(); - final DdlType ddlType = ddlTypeRegistry.getDescriptor( - basicType.getJdbcType().getDdlTypeCode() - ); + final var basicType = (BasicType) type.getSingleJdbcMapping(); + final var ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry(); + final var ddlType = ddlTypeRegistry.getDescriptor( basicType.getJdbcType().getDdlTypeCode() ); return ddlType.getTypeName( size, basicType, ddlTypeRegistry ); } } @@ -97,11 +87,9 @@ public static String getTypeName(ReturnableType type, Size size, TypeConfigur return AbstractSqlAstTranslator.getSqlTypeName( sqlTypedMapping, typeConfiguration ); } else { - final BasicType basicType = (BasicType) ( (JdbcMappingContainer) type ).getSingleJdbcMapping(); - final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry(); - final DdlType ddlType = ddlTypeRegistry.getDescriptor( - basicType.getJdbcType().getDdlTypeCode() - ); + final var basicType = (BasicType) ( (JdbcMappingContainer) type ).getSingleJdbcMapping(); + final var ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry(); + final var ddlType = ddlTypeRegistry.getDescriptor( basicType.getJdbcType().getDdlTypeCode() ); return ddlType.getTypeName( size, basicType, ddlTypeRegistry ); } } @@ -123,11 +111,9 @@ public static String getCastTypeName(JdbcMappingContainer type, Size size, TypeC return AbstractSqlAstTranslator.getCastTypeName( sqlTypedMapping, typeConfiguration ); } else { - final BasicType basicType = (BasicType) type.getSingleJdbcMapping(); - final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry(); - final DdlType ddlType = ddlTypeRegistry.getDescriptor( - basicType.getJdbcType().getDdlTypeCode() - ); + final var basicType = (BasicType) type.getSingleJdbcMapping(); + final var ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry(); + final var ddlType = ddlTypeRegistry.getDescriptor( basicType.getJdbcType().getDdlTypeCode() ); return ddlType.getCastTypeName( size, basicType, ddlTypeRegistry ); } } @@ -141,11 +127,9 @@ public static String getCastTypeName(ReturnableType type, Size size, TypeConf return AbstractSqlAstTranslator.getCastTypeName( sqlTypedMapping, typeConfiguration ); } else { - final BasicType basicType = (BasicType) ( (JdbcMappingContainer) type ).getSingleJdbcMapping(); - final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry(); - final DdlType ddlType = ddlTypeRegistry.getDescriptor( - basicType.getJdbcType().getDdlTypeCode() - ); + final var basicType = (BasicType) ( (JdbcMappingContainer) type ).getSingleJdbcMapping(); + final var ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry(); + final var ddlType = ddlTypeRegistry.getDescriptor( basicType.getJdbcType().getDdlTypeCode() ); return ddlType.getCastTypeName( size, basicType, ddlTypeRegistry ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/JsonArrayViaElementArgumentReturnTypeResolver.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/JsonArrayViaElementArgumentReturnTypeResolver.java index ce7ed772650b..4ddb37bf909e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/JsonArrayViaElementArgumentReturnTypeResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/JsonArrayViaElementArgumentReturnTypeResolver.java @@ -7,9 +7,7 @@ import java.util.List; import java.util.function.Supplier; -import org.hibernate.internal.build.AllowReflection; import org.hibernate.metamodel.mapping.BasicValuedMapping; -import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.ReturnableType; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; @@ -20,7 +18,6 @@ import org.hibernate.type.SqlTypes; import org.hibernate.type.descriptor.java.BasicPluralJavaType; import org.hibernate.type.descriptor.jdbc.DelegatingJdbcTypeIndicators; -import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; import org.hibernate.type.spi.TypeConfiguration; import org.checkerframework.checker.nullness.qual.Nullable; @@ -44,12 +41,12 @@ public ReturnableType resolveFunctionReturnType( TypeConfiguration typeConfiguration) { if ( converter != null ) { if ( converter.isInTypeInference() ) { - // Don't default to a Json array when in type inference mode. + // Don't default to a JSON array when in type inference mode. // Comparing e.g. `array() = (select array_agg() ...)` will trigger this resolver // while inferring the type for `array()`, which we want to avoid. return null; } - final MappingModelExpressible inferredType = converter.resolveFunctionImpliedReturnType(); + final var inferredType = converter.resolveFunctionImpliedReturnType(); if ( inferredType != null ) { if ( inferredType instanceof ReturnableType returnableType ) { return returnableType; @@ -62,8 +59,8 @@ else if ( inferredType instanceof BasicValuedMapping basicValuedMapping ) { if ( impliedType != null ) { return impliedType; } - for ( SqmTypedNode argument : arguments ) { - final DomainType sqmType = argument.getExpressible().getSqmType(); + for ( var argument : arguments ) { + final var sqmType = argument.getExpressible().getSqmType(); if ( sqmType instanceof ReturnableType ) { return resolveJsonArrayType( sqmType, typeConfiguration ); } @@ -78,14 +75,14 @@ public BasicValuedMapping resolveFunctionReturnType( return null; } - @AllowReflection +// @AllowReflection public static BasicType resolveJsonArrayType(DomainType elementType, TypeConfiguration typeConfiguration) { @SuppressWarnings("unchecked") final var arrayJavaType = (BasicPluralJavaType) typeConfiguration.getJavaTypeRegistry() .resolveArrayDescriptor( elementType.getJavaType() ); - final JdbcTypeIndicators currentBaseSqlTypeIndicators = typeConfiguration.getCurrentBaseSqlTypeIndicators(); + final var currentBaseSqlTypeIndicators = typeConfiguration.getCurrentBaseSqlTypeIndicators(); return arrayJavaType.resolveType( typeConfiguration, currentBaseSqlTypeIndicators.getDialect(), diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/type/PrimitiveWrapperHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/type/PrimitiveWrapperHelper.java deleted file mode 100644 index d2e88c50ea20..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/type/PrimitiveWrapperHelper.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.internal.util.type; - -/** - * Helper for primitive/wrapper utilities. - * - * @author Steve Ebersole - */ -public final class PrimitiveWrapperHelper { - private PrimitiveWrapperHelper() { - } - - /** - * Describes a particular primitive/wrapper combo - */ - public interface PrimitiveWrapperDescriptor { - Class getPrimitiveClass(); - Class getWrapperClass(); - } - - public static X cast(Class type, Object value) { - return type.isPrimitive() - ? getDescriptorByPrimitiveType( type ).getWrapperClass().cast( value ) - : type.cast( value ); - } - - public static boolean isInstance(Class type, Object value) { - return type.isPrimitive() - ? getDescriptorByPrimitiveType( type ).getWrapperClass().isInstance( value ) - : type.isInstance( value ); - } - - public static class BooleanDescriptor implements PrimitiveWrapperDescriptor { - public static final BooleanDescriptor INSTANCE = new BooleanDescriptor(); - - private BooleanDescriptor() { - } - - @Override - public Class getPrimitiveClass() { - return boolean.class; - } - - @Override - public Class getWrapperClass() { - return Boolean.class; - } - } - - public static class CharacterDescriptor implements PrimitiveWrapperDescriptor { - public static final CharacterDescriptor INSTANCE = new CharacterDescriptor(); - - private CharacterDescriptor() { - } - - @Override - public Class getPrimitiveClass() { - return char.class; - } - - @Override - public Class getWrapperClass() { - return Character.class; - } - } - - public static class ByteDescriptor implements PrimitiveWrapperDescriptor { - public static final ByteDescriptor INSTANCE = new ByteDescriptor(); - - private ByteDescriptor() { - } - - @Override - public Class getPrimitiveClass() { - return byte.class; - } - - @Override - public Class getWrapperClass() { - return Byte.class; - } - } - - public static class ShortDescriptor implements PrimitiveWrapperDescriptor { - public static final ShortDescriptor INSTANCE = new ShortDescriptor(); - - private ShortDescriptor() { - } - - @Override - public Class getPrimitiveClass() { - return short.class; - } - - @Override - public Class getWrapperClass() { - return Short.class; - } - } - - public static class IntegerDescriptor implements PrimitiveWrapperDescriptor { - public static final IntegerDescriptor INSTANCE = new IntegerDescriptor(); - - private IntegerDescriptor() { - } - - @Override - public Class getPrimitiveClass() { - return int.class; - } - - @Override - public Class getWrapperClass() { - return Integer.class; - } - } - - public static class LongDescriptor implements PrimitiveWrapperDescriptor { - public static final LongDescriptor INSTANCE = new LongDescriptor(); - - private LongDescriptor() { - } - - @Override - public Class getPrimitiveClass() { - return long.class; - } - - @Override - public Class getWrapperClass() { - return Long.class; - } - } - - public static class FloatDescriptor implements PrimitiveWrapperDescriptor { - public static final FloatDescriptor INSTANCE = new FloatDescriptor(); - - private FloatDescriptor() { - } - - @Override - public Class getPrimitiveClass() { - return float.class; - } - - @Override - public Class getWrapperClass() { - return Float.class; - } - } - - public static class DoubleDescriptor implements PrimitiveWrapperDescriptor { - public static final DoubleDescriptor INSTANCE = new DoubleDescriptor(); - - private DoubleDescriptor() { - } - - @Override - public Class getPrimitiveClass() { - return double.class; - } - - @Override - public Class getWrapperClass() { - return Double.class; - } - } - - @SuppressWarnings("unchecked") - public static PrimitiveWrapperDescriptor getDescriptorByPrimitiveType(Class primitiveClazz) { - if ( ! primitiveClazz.isPrimitive() ) { - throw new IllegalArgumentException( "Given class is not a primitive type : " + primitiveClazz.getName() ); - } - - if ( boolean.class == primitiveClazz ) { - return (PrimitiveWrapperDescriptor) BooleanDescriptor.INSTANCE; - } - - if ( char.class == primitiveClazz ) { - return (PrimitiveWrapperDescriptor) CharacterDescriptor.INSTANCE; - } - - if ( byte.class == primitiveClazz ) { - return (PrimitiveWrapperDescriptor) ByteDescriptor.INSTANCE; - } - - if ( short.class == primitiveClazz ) { - return (PrimitiveWrapperDescriptor) ShortDescriptor.INSTANCE; - } - - if ( int.class == primitiveClazz ) { - return (PrimitiveWrapperDescriptor) IntegerDescriptor.INSTANCE; - } - - if ( long.class == primitiveClazz ) { - return (PrimitiveWrapperDescriptor) LongDescriptor.INSTANCE; - } - - if ( float.class == primitiveClazz ) { - return (PrimitiveWrapperDescriptor) FloatDescriptor.INSTANCE; - } - - if ( double.class == primitiveClazz ) { - return (PrimitiveWrapperDescriptor) DoubleDescriptor.INSTANCE; - } - - if ( void.class == primitiveClazz ) { - throw new IllegalArgumentException( "void, as primitive type, has no wrapper equivalent" ); - } - - throw new IllegalArgumentException( "Unrecognized primitive type class : " + primitiveClazz.getName() ); - } - - @SuppressWarnings("unchecked") - public static PrimitiveWrapperDescriptor getDescriptorByWrapperType(Class wrapperClass) { - if ( wrapperClass.isPrimitive() ) { - throw new IllegalArgumentException( "Given class is a primitive type : " + wrapperClass.getName() ); - } - - if ( Boolean.class.equals( wrapperClass ) ) { - return (PrimitiveWrapperDescriptor) BooleanDescriptor.INSTANCE; - } - - if ( Character.class == wrapperClass ) { - return (PrimitiveWrapperDescriptor) CharacterDescriptor.INSTANCE; - } - - if ( Byte.class == wrapperClass ) { - return (PrimitiveWrapperDescriptor) ByteDescriptor.INSTANCE; - } - - if ( Short.class == wrapperClass ) { - return (PrimitiveWrapperDescriptor) ShortDescriptor.INSTANCE; - } - - if ( Integer.class == wrapperClass ) { - return (PrimitiveWrapperDescriptor) IntegerDescriptor.INSTANCE; - } - - if ( Long.class == wrapperClass ) { - return (PrimitiveWrapperDescriptor) LongDescriptor.INSTANCE; - } - - if ( Float.class == wrapperClass ) { - return (PrimitiveWrapperDescriptor) FloatDescriptor.INSTANCE; - } - - if ( Double.class == wrapperClass ) { - return (PrimitiveWrapperDescriptor) DoubleDescriptor.INSTANCE; - } - - // most likely Void.class, which we can't really handle here - throw new IllegalArgumentException( "Unrecognized wrapper type class : " + wrapperClass.getName() ); - } - - public static boolean isWrapper(Class clazz) { - try { - getDescriptorByWrapperType( clazz ); - return true; - } - catch (Exception e) { - return false; - } - } - - public static boolean arePrimitiveWrapperEquivalents(Class converterDefinedType, Class propertyType) { - if ( converterDefinedType.isPrimitive() ) { - return getDescriptorByPrimitiveType( converterDefinedType ).getWrapperClass().equals( propertyType ); - } - else if ( propertyType.isPrimitive() ) { - return getDescriptorByPrimitiveType( propertyType ).getWrapperClass().equals( converterDefinedType ); - } - return false; - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/type/PrimitiveWrappers.java b/hibernate-core/src/main/java/org/hibernate/internal/util/type/PrimitiveWrappers.java new file mode 100644 index 000000000000..163f1c35caeb --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/type/PrimitiveWrappers.java @@ -0,0 +1,47 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.internal.util.type; + +/** + * Maps primitive types to their wrapper counterparts. + * + * @author Gavin King + */ +public final class PrimitiveWrappers { + + public static Class canonicalize(Class type) { + if ( type.isPrimitive() ) { + @SuppressWarnings("unchecked") + // completely safe, boolean.class is a Class + final var wrapperClass = (Class) wrapperClass( type ); + return wrapperClass; + } + else { + return type; + } + } + + private static Class wrapperClass(Class primitiveClass) { + return switch ( primitiveClass.getName() ) { + case "boolean" -> Boolean.class; + case "char" -> Character.class; + case "byte" -> Byte.class; + case "short" -> Short.class; + case "int" -> Integer.class; + case "long" -> Long.class; + case "float" -> Float.class; + case "double" -> Double.class; + default -> throw new AssertionError( "Unknown primitive type: " + primitiveClass ); + }; + } + + public static X cast(Class type, Object value) { + return canonicalize( type ).cast( value ); + } + + public static boolean isInstance(Class type, Object value) { + return canonicalize( type ).isInstance( value ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java index 30887186f042..e6853604cd6c 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappingMetamodelImpl.java @@ -35,6 +35,7 @@ import org.hibernate.metamodel.model.domain.JpaMetamodel; import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.metamodel.model.domain.NavigableRole; +import org.hibernate.query.sqm.tree.domain.SqmManagedDomainType; import org.hibernate.type.BindingContext; import org.hibernate.query.sqm.tuple.TupleType; import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor; @@ -113,12 +114,12 @@ public class MappingMetamodelImpl // - ignoring Hibernate's representation mode (entity mode), the Class // object for an entity (or mapped superclass) always refers to the same // JPA EntityType and Hibernate EntityPersister. The problem arises with - // embeddables. For an embeddable, as with the rest of its metamodel, + // embeddables. For an embeddable type, as with the rest of its metamodel, // Hibernate combines the embeddable's relational/mapping while JPA does // not. This is perfectly consistent with each paradigm. But it results // in a mismatch since JPA expects a single "type descriptor" for a - // given embeddable class while Hibernate incorporates the - // relational/mapping info so we have a "type descriptor" for each usage + // given embeddable class, while Hibernate incorporates the + // relational/mapping info, so we have a "type descriptor" for each usage // of that embeddable type. (Think embeddable versus embedded.) // // To account for this, we track both paradigms here. @@ -258,11 +259,9 @@ private void processBootCollections( for ( final var model : collectionBindings ) { final String role = model.getRole(); final var persister = - persisterFactory.createCollectionPersister( - model, + persisterFactory.createCollectionPersister( model, cacheImplementor.getCollectionRegionAccess( new NavigableRole( role ) ), - modelCreationContext - ); + modelCreationContext ); collectionPersisterMap.put( role, persister ); if ( persister.getIndexType() instanceof org.hibernate.type.EntityType entityType ) { registerEntityParticipant( entityType, persister ); @@ -360,32 +359,29 @@ public boolean isEntityClass(Class entityJavaType) { @Override public EntityPersister getEntityDescriptor(Class entityJavaType) { - var entityPersister = entityPersisterMap.get( entityJavaType.getName() ); - if ( entityPersister == null ) { - final String mappedEntityName = entityProxyInterfaceMap.get( entityJavaType ); - if ( mappedEntityName != null ) { - entityPersister = entityPersisterMap.get( mappedEntityName ); - } - } - if ( entityPersister == null ) { - throw new UnknownEntityTypeException( entityJavaType ); - } - return entityPersister; + return getEntityPersister( entityJavaType ); } @Override @Deprecated(forRemoval = true) @SuppressWarnings( "removal" ) public EntityPersister locateEntityDescriptor(Class byClass) { - var entityPersister = entityPersisterMap.get( byClass.getName() ); - if ( entityPersister == null ) { + return getEntityPersister( byClass ); + } + + private EntityPersister getEntityPersister(Class byClass) { + final var entityPersister = entityPersisterMap.get( byClass.getName() ); + if ( entityPersister != null ) { + return entityPersister; + } + else { final String mappedEntityName = entityProxyInterfaceMap.get( byClass ); if ( mappedEntityName != null ) { - entityPersister = entityPersisterMap.get( mappedEntityName ); + final var persister = entityPersisterMap.get( mappedEntityName ); + if ( persister != null ) { + return persister; + } } - } - if ( entityPersister == null ) { throw new UnknownEntityTypeException( byClass ); } - return entityPersister; } @Override @@ -644,7 +640,7 @@ public String[] getAllCollectionRoles() { public @Nullable BindableType resolveParameterBindType(Class javaType) { final var typeConfiguration = getTypeConfiguration(); - final BasicType basicType = typeConfiguration.getBasicTypeForJavaType( javaType ); + final var basicType = typeConfiguration.getBasicTypeForJavaType( javaType ); // For enums, we simply don't know the exact mapping if there is no basic type registered if ( basicType != null || javaType.isEnum() ) { return basicType; @@ -652,7 +648,7 @@ public String[] getAllCollectionRoles() { final var managedType = jpaMetamodel.findManagedType( javaType ); if ( managedType != null ) { - return (BindableType) managedType; + return (SqmManagedDomainType) managedType; } final var javaTypeRegistry = typeConfiguration.getJavaTypeRegistry(); @@ -685,12 +681,12 @@ public String[] getAllCollectionRoles() { return null; } - final Class clazz = unproxiedClass( bindValue ); - - // Resolve superclass bindable type if necessary, as we don't register types for e.g. Inet4Address + final var clazz = unproxiedClass( bindValue ); + // Resolve the superclass bindable type if necessary, + // as we don't register types for e.g. Inet4Address Class c = clazz; do { - final BindableType type = resolveParameterBindType( c ); + final var type = resolveParameterBindType( c ); if ( type != null ) { return type; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/specification/internal/ProjectionSpecificationImpl.java b/hibernate-core/src/main/java/org/hibernate/query/specification/internal/ProjectionSpecificationImpl.java index 07fb2d33babc..e97c3e821e31 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/specification/internal/ProjectionSpecificationImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/specification/internal/ProjectionSpecificationImpl.java @@ -31,7 +31,7 @@ import java.util.Map; import java.util.function.BiFunction; -import static org.hibernate.internal.util.type.PrimitiveWrapperHelper.cast; +import static org.hibernate.internal.util.type.PrimitiveWrappers.cast; /** * @author Gavin King diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java index 806fb177c50a..6fae0b5323e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java @@ -127,7 +127,7 @@ import static org.hibernate.internal.util.collections.CollectionHelper.makeCopy; import static org.hibernate.internal.util.collections.CollectionHelper.toSmallList; import static org.hibernate.internal.util.collections.CollectionHelper.toSmallMap; -import static org.hibernate.internal.util.type.PrimitiveWrapperHelper.getDescriptorByPrimitiveType; +import static org.hibernate.internal.util.type.PrimitiveWrappers.canonicalize; import static org.hibernate.jpa.HibernateHints.HINT_NATIVE_LOCK_MODE; import static org.hibernate.query.results.internal.Builders.resultClassBuilder; import static org.hibernate.query.results.ResultSetMapping.resolveResultSetMapping; @@ -384,11 +384,7 @@ private boolean validConstructorFoundForResultType(Class resultType, ResultSe } private static boolean constructorParameterMatches(ResultBuilder resultBuilder, Class paramType) { - final var parameterClass = - paramType.isPrimitive() - ? getDescriptorByPrimitiveType( paramType ).getWrapperClass() - : paramType; - return resultBuilder.getJavaType() == parameterClass; + return resultBuilder.getJavaType() == canonicalize( paramType ); } protected void setTupleTransformerForResultType(Class resultClass) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java index 0bcb51539151..c789c4911cef 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/TypecheckUtil.java @@ -36,6 +36,7 @@ import java.time.temporal.TemporalAmount; import java.util.Objects; +import static org.hibernate.internal.util.type.PrimitiveWrappers.canonicalize; import static org.hibernate.type.descriptor.java.JavaTypeHelper.isUnknown; /** @@ -383,20 +384,6 @@ private static boolean isConvertedType(SqmExpressible type) { return type.getSqmType() instanceof ConvertedBasicType; } - private static Class canonicalize(Class lhs) { - return switch (lhs.getCanonicalName()) { - case "boolean" -> Boolean.class; - case "byte" -> Byte.class; - case "short" -> Short.class; - case "int" -> Integer.class; - case "long" -> Long.class; - case "float" -> Float.class; - case "double" -> Double.class; - case "char" -> Character.class; - default -> lhs; - }; - } - private static boolean isMappedSuperclassTypeAssignable( MappedSuperclassDomainType lhsType, EntityType rhsType, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 35ad112e177b..e540e6d58bc7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -105,7 +105,6 @@ import org.hibernate.query.sqm.CastType; import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.DiscriminatorSqmPath; -import org.hibernate.query.sqm.DynamicInstantiationNature; import org.hibernate.query.sqm.InterpretationException; import org.hibernate.query.sqm.SqmBindableType; import org.hibernate.query.sqm.SqmExpressible; @@ -252,7 +251,6 @@ import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; import org.hibernate.query.sqm.tree.select.SqmAliasedNode; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation; -import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiationArgument; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiationTarget; import org.hibernate.query.sqm.tree.select.SqmJpaCompoundSelection; import org.hibernate.query.sqm.tree.select.SqmOrderByClause; @@ -1632,12 +1630,12 @@ public Values visitValues(SqmValues sqmValues) { @Override public SelectStatement visitSelectStatement(SqmSelectStatement statement) { - final CteContainer oldCteContainer = cteContainer; - final CteContainer cteContainer = this.visitCteContainer( statement ); - final SqmStatement oldSqmStatement = this.currentSqmStatement; + final var oldCteContainer = cteContainer; + final var cteContainer = this.visitCteContainer( statement ); + final var oldSqmStatement = this.currentSqmStatement; this.currentSqmStatement = statement; - final QueryPart queryPart = visitQueryPart( statement.getQueryPart() ); + final var queryPart = visitQueryPart( statement.getQueryPart() ); final List> domainResults = queryPart.isRoot() ? this.domainResults : emptyList(); try { return new SelectStatement( cteContainer, queryPart, domainResults ); @@ -1651,18 +1649,17 @@ public SelectStatement visitSelectStatement(SqmSelectStatement statement) { @Override public DynamicInstantiation visitDynamicInstantiation(SqmDynamicInstantiation sqmDynamicInstantiation) { - final SqmDynamicInstantiationTarget instantiationTarget = sqmDynamicInstantiation.getInstantiationTarget(); - final DynamicInstantiationNature instantiationNature = instantiationTarget.getNature(); - final JavaType targetTypeDescriptor = interpretInstantiationTarget( instantiationTarget ); + final var instantiationTarget = sqmDynamicInstantiation.getInstantiationTarget(); + final var instantiationNature = instantiationTarget.getNature(); + final var targetTypeDescriptor = interpretInstantiationTarget( instantiationTarget ); - final DynamicInstantiation dynamicInstantiation = - new DynamicInstantiation<>( instantiationNature, targetTypeDescriptor ); + final var dynamicInstantiation = new DynamicInstantiation<>( instantiationNature, targetTypeDescriptor ); - for ( SqmDynamicInstantiationArgument sqmArgument : sqmDynamicInstantiation.getArguments() ) { + for ( var sqmArgument : sqmDynamicInstantiation.getArguments() ) { if ( sqmArgument.getSelectableNode() instanceof SqmPath sqmPath ) { prepareForSelection( sqmPath ); } - final DomainResultProducer argumentResultProducer = (DomainResultProducer) sqmArgument.accept( this ); + final var argumentResultProducer = (DomainResultProducer) sqmArgument.accept( this ); dynamicInstantiation.addArgument( sqmArgument.getAlias(), argumentResultProducer, this ); } @@ -1672,9 +1669,9 @@ public DynamicInstantiation visitDynamicInstantiation(SqmDynamicInstantiation return dynamicInstantiation; } - private JavaType interpretInstantiationTarget(SqmDynamicInstantiationTarget instantiationTarget) { + private JavaType interpretInstantiationTarget(SqmDynamicInstantiationTarget instantiationTarget) { return getCreationContext().getTypeConfiguration().getJavaTypeRegistry() - .getDescriptor( switch ( instantiationTarget.getNature() ) { + .resolveDescriptor( switch ( instantiationTarget.getNature() ) { case LIST -> List.class; case MAP -> Map.class; default -> instantiationTarget.getJavaType(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleImpl.java index f11ce68c3f5c..d53ed305f766 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/TupleImpl.java @@ -10,8 +10,8 @@ import jakarta.persistence.Tuple; import jakarta.persistence.TupleElement; -import static org.hibernate.internal.util.type.PrimitiveWrapperHelper.cast; -import static org.hibernate.internal.util.type.PrimitiveWrapperHelper.isInstance; +import static org.hibernate.internal.util.type.PrimitiveWrappers.cast; +import static org.hibernate.internal.util.type.PrimitiveWrappers.isInstance; /** * Implementation of the JPA Tuple contract diff --git a/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java b/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java index 62a4ceed34ee..5da4df670708 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/AbstractStandardBasicType.java @@ -289,7 +289,7 @@ public final void beforeAssemble(Serializable cached, SharedSessionContractImple public final Object replace(Object original, Object target, SharedSessionContractImplementor session, Object owner, Map copyCache) { return original == null && target == null ? null - : javaType.getReplacement( javaType.cast( original ) , javaType.cast( target ) , session ); + : javaType.getReplacement( javaType.cast( original ), javaType.cast( target ), session ); } @@ -341,7 +341,7 @@ public void nullSafeSet(CallableStatement st, T value, String name, SharedSessio } protected final void nullSafeSet(CallableStatement st, Object value, String name, WrapperOptions options) throws SQLException { - getJdbcValueBinder().bind( st, javaType.cast( value ) , name, options ); + getJdbcValueBinder().bind( st, javaType.cast( value ), name, options ); } @Override @@ -363,8 +363,7 @@ public CastType getCastType() { // and the cast type determination here. Note that we interpret the converter in ConvertedBasicTypeImpl // to properly determine the correct cast type final var jdbcType = getJdbcType(); - final int jdbcTypeCode = jdbcType.getDdlTypeCode(); - switch ( jdbcTypeCode ) { + switch ( jdbcType.getDdlTypeCode() ) { case Types.BIT: case Types.SMALLINT: case Types.TINYINT: diff --git a/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java b/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java index b4603acc2217..1127a18db077 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java @@ -137,6 +137,7 @@ public BasicType resolve(Class javaType, int sqlTypeCode) { return resolve( getJavaTypeRegistry().resolveDescriptor( javaType ), sqlTypeCode ); } + @Deprecated(since = "7.2", forRemoval = true) // no longer used + calls deprecated method public BasicType resolve(java.lang.reflect.Type javaType, int sqlTypeCode) { return resolve( getJavaTypeRegistry().getDescriptor( javaType ), sqlTypeCode ); } @@ -406,7 +407,8 @@ private void applyRegistrationKeys(BasicType type, String[] keys) { // Incredibly verbose logging disabled // LOG.tracef( "Adding type registration %s -> %s", key, type ); - final Type old = typesByName.put( key, type ); +// final Type old = + typesByName.put( key, type ); // if ( old != null && old != type ) { // LOG.tracef( // "Type registration key [%s] overrode previous entry : `%s`", @@ -430,7 +432,8 @@ private void applyRegistrationKeys(BasicTypeReference type, String[] keys) { // Incredibly verbose logging disabled // LOG.tracef( "Adding type registration %s -> %s", key, type ); - final BasicTypeReference old = typeReferencesByName.put( key, type ); +// final BasicTypeReference old = + typeReferencesByName.put( key, type ); // if ( old != null && old != type ) { // LOG.tracef( // "Type registration key [%s] overrode previous entry : `%s`", diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/AttributeConverterBean.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/AttributeConverterBean.java index 51b9a3fb5909..0cdc917c1cda 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/AttributeConverterBean.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/AttributeConverterBean.java @@ -74,14 +74,16 @@ private JavaType getTypeDescriptor( private MutabilityPlan getMutabilityPlan( ManagedBean> attributeConverterBean, JpaAttributeConverterCreationContext context) { - final MutabilityPlan mutabilityPlan = + final var mutabilityPlan = RegistryHelper.INSTANCE.determineMutabilityPlan( - attributeConverterBean.getBeanClass(), + (java.lang.reflect.Type) + attributeConverterBean.getBeanClass(), context.getTypeConfiguration() ); + //noinspection unchecked return mutabilityPlan == null ? new AttributeConverterMutabilityPlan<>( this, true ) - : mutabilityPlan; + : (MutabilityPlan) mutabilityPlan; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/MutableMutabilityPlan.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/MutableMutabilityPlan.java index ff826c2595ef..1d129e72daf1 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/MutableMutabilityPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/MutableMutabilityPlan.java @@ -15,7 +15,12 @@ */ public abstract class MutableMutabilityPlan implements MutabilityPlan { - public static final MutableMutabilityPlan INSTANCE = new MutableMutabilityPlan<>() { + public static MutableMutabilityPlan instance() { + //noinspection unchecked + return INSTANCE; + } + + public static final MutableMutabilityPlan INSTANCE = new MutableMutabilityPlan<>() { @Override protected Object deepCopyNotNull(Object value) { return value; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java index d4f9a95dcc22..cfaad573f0d2 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java @@ -4,6 +4,15 @@ */ package org.hibernate.type.descriptor.java.spi; +import org.hibernate.Internal; +import org.hibernate.type.descriptor.java.ArrayJavaType; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.java.MutabilityPlan; +import org.hibernate.type.descriptor.java.MutableMutabilityPlan; +import org.hibernate.type.spi.TypeConfiguration; +import org.hibernate.type.spi.TypeConfigurationAware; +import org.jboss.logging.Logger; + import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -12,14 +21,8 @@ import java.util.function.Consumer; import java.util.function.Supplier; -import org.hibernate.type.descriptor.java.ArrayJavaType; -import org.hibernate.type.descriptor.java.JavaType; -import org.hibernate.type.descriptor.java.MutabilityPlan; -import org.hibernate.type.descriptor.java.MutableMutabilityPlan; -import org.hibernate.type.spi.TypeConfiguration; -import org.hibernate.type.spi.TypeConfigurationAware; +import static org.hibernate.internal.util.type.PrimitiveWrappers.canonicalize; -import org.jboss.logging.Logger; /** * A registry mapping {@link Class Java classes} to implementations @@ -75,7 +78,7 @@ public void forEachDescriptor(Consumer> consumer) { } public void addDescriptor(JavaType descriptor) { - final JavaType old = descriptorsByTypeName.put( descriptor.getJavaType().getTypeName(), descriptor ); + final var old = descriptorsByTypeName.put( descriptor.getJavaType().getTypeName(), descriptor ); if ( old != null ) { LOG.debugf( "JavaTypeRegistry entry replaced : %s -> %s (was %s)", @@ -87,7 +90,10 @@ public void addDescriptor(JavaType descriptor) { performInjections( descriptor ); } - @Deprecated(since = "7.2") // Due to unbound type parameter + // This method is terribly lacking in type safety but + // has wormed itself into so many clients that I can't + // easily do away with it right now + @Deprecated(since = "7.2") // Due to unbound type parameter and unchecked cast public JavaType getDescriptor(Type javaType) { //noinspection unchecked return (JavaType) resolveDescriptor( javaType ); @@ -97,14 +103,36 @@ public JavaType findDescriptor(Type javaType) { return descriptorsByTypeName.get( javaType.getTypeName() ); } - public JavaType findDescriptor(Class javaType) { - //noinspection unchecked - return (JavaType) findDescriptor( (Type) javaType ); + public JavaType findDescriptor(Class javaClass) { + final var cached = descriptorsByTypeName.get( javaClass.getTypeName() ); + return cached == null ? null : checkCached( javaClass, cached ); } public JavaType resolveDescriptor(Class javaType, Supplier> creator) { - //noinspection unchecked - return (JavaType) resolveDescriptor( javaType.getTypeName(), creator ); + final String javaTypeName = javaType.getTypeName(); + final var cached = descriptorsByTypeName.get( javaTypeName ); + if ( cached != null ) { + return checkCached( javaType, cached ); + } + else { + final var created = creator.get(); + descriptorsByTypeName.put( javaTypeName, created ); + return created; + } + } + + private static JavaType checkCached(Class javaClass, JavaType cached) { + final var cachedClass = cached.getJavaTypeClass(); + if ( !isCompatible( javaClass, cachedClass ) ) { + throw new IllegalStateException( "Type registration was corrupted for: " + javaClass.getName() ); + } + @SuppressWarnings("unchecked") // safe, we just checked + final var resolvedType = (JavaType) cached; + return resolvedType; + } + + private static boolean isCompatible(Class givenClass, Class cachedClass) { + return cachedClass == canonicalize( givenClass ); } @Deprecated(since = "7.2", forRemoval = true) // Can be private @@ -125,25 +153,18 @@ public JavaType resolveDescriptor(Type javaType) { } public JavaType resolveDescriptor(Class javaType) { - //noinspection unchecked - return (JavaType) resolveDescriptor( javaType, JavaTypeRegistry::createMutabilityPlan ); + return resolveDescriptor( javaType, JavaTypeRegistry::createMutabilityPlan ); } public JavaType resolveDescriptor(JavaType javaType) { - //noinspection unchecked - return (JavaType) resolveDescriptor( javaType.getJavaTypeClass().getTypeName(), () -> javaType ); + return resolveDescriptor( javaType.getJavaTypeClass(), () -> javaType ); } private static MutabilityPlan createMutabilityPlan(Type elementJavaType, TypeConfiguration typeConfiguration) { - final MutabilityPlan determinedPlan = RegistryHelper.INSTANCE.determineMutabilityPlan( - elementJavaType, - typeConfiguration - ); - if ( determinedPlan != null ) { - return determinedPlan; - } + final var determinedPlan = + RegistryHelper.INSTANCE.determineMutabilityPlan( elementJavaType, typeConfiguration ); + return determinedPlan != null ? determinedPlan : MutableMutabilityPlan.instance(); - return MutableMutabilityPlan.INSTANCE; } public JavaType resolveArrayDescriptor(Class elementJavaType) { @@ -153,7 +174,26 @@ public JavaType resolveArrayDescriptor(Class elementJavaType) { () -> createArrayTypeDescriptor( elementJavaType, JavaTypeRegistry::createMutabilityPlan) ); } - @Deprecated(since = "7.2", forRemoval = true) // Can be private + // WAS: +// public JavaType resolveArrayDescriptor(Class elementJavaType) { +// return resolveDescriptor( arrayClass( elementJavaType ), +// () -> createArrayTypeDescriptor( elementJavaType, JavaTypeRegistry::createMutabilityPlan) ); +// } + + @Internal // Can be demoted to private + public JavaType resolveDescriptor( + Class javaType, + BiFunction> mutabilityPlanCreator) { + //noinspection unchecked + return resolveDescriptor( + javaType, + () -> javaType.isArray() + ? (JavaType) createArrayTypeDescriptor( javaType.getComponentType(), mutabilityPlanCreator ) + : createTypeDescriptor( javaType, mutabilityPlanCreator ) + ); + } + + @Internal // Can be demoted to private public JavaType resolveDescriptor( Type javaType, BiFunction> mutabilityPlanCreator) { @@ -175,7 +215,7 @@ else if ( javaType instanceof Class javaClass && javaClass.isArray() ) { } private JavaType createArrayTypeDescriptor(Class elementJavaType, BiFunction> mutabilityPlanCreator) { - JavaType elementTypeDescriptor = findDescriptor( elementJavaType ); + var elementTypeDescriptor = findDescriptor( elementJavaType ); if ( elementTypeDescriptor == null ) { elementTypeDescriptor = createTypeDescriptor( elementJavaType, mutabilityPlanCreator ); } @@ -186,30 +226,55 @@ private JavaType createTypeDescriptor(Type javaType, BiFunction (MutabilityPlan) mutabilityPlanCreator.apply( javaType, typeConfiguration ), + () -> (MutabilityPlan) + mutabilityPlanCreator.apply( javaType, typeConfiguration ), typeConfiguration ); } - public JavaType resolveManagedTypeDescriptor(Type javaType) { - return resolveManagedTypeDescriptor( javaType, false ); + public JavaType resolveManagedTypeDescriptor(Class javaType) { + return resolveManagedTypeDescriptor( javaType, JavaTypeBasicAdaptor::new ); } - public JavaType resolveManagedTypeDescriptor(Class javaType) { - //noinspection unchecked - return (JavaType) resolveManagedTypeDescriptor( javaType, false ); + public JavaType resolveEntityTypeDescriptor(Class javaType) { + return resolveManagedTypeDescriptor( javaType, EntityJavaType::new ); } - public JavaType resolveEntityTypeDescriptor(Type javaType) { - return resolveManagedTypeDescriptor( javaType, true ); + private JavaType resolveManagedTypeDescriptor( + Class javaType, + BiFunction, MutabilityPlan, JavaType> instantiate) { + return resolveDescriptor( + javaType, + () -> { + final var determinedPlan = + RegistryHelper.INSTANCE.determineMutabilityPlan( javaType, typeConfiguration ); + return instantiate.apply( + javaType, + determinedPlan != null + ? determinedPlan + : MutableMutabilityPlan.instance() + ); + } + ); } - public JavaType resolveEntityTypeDescriptor(Class javaType) { - //noinspection unchecked - return (JavaType) resolveManagedTypeDescriptor( javaType, true ); + // CAN BE REMOVED: + + @Deprecated(since = "7.2", forRemoval = true) // no longer used + public JavaType resolveManagedTypeDescriptor(Type javaType) { + return resolveManagedTypeDescriptor( javaType, JavaTypeBasicAdaptor::new ); + } + + @Deprecated(since = "7.2", forRemoval = true) // no longer used + public JavaType resolveEntityTypeDescriptor(Type javaType) { + return resolveManagedTypeDescriptor( javaType, EntityJavaType::new ); } - private JavaType resolveManagedTypeDescriptor(Type javaType, boolean entity) { + @Deprecated(since = "7.2", forRemoval = true) // no longer used + @SuppressWarnings("unchecked") + private JavaType resolveManagedTypeDescriptor( + Type javaType, + BiFunction, MutabilityPlan, JavaType> instantiate) { return resolveDescriptor( javaType.getTypeName(), () -> { @@ -222,18 +287,13 @@ private JavaType resolveManagedTypeDescriptor(Type javaType, boolean enti javaTypeClass = (Class) parameterizedType.getRawType(); } - final MutabilityPlan determinedPlan = RegistryHelper.INSTANCE.determineMutabilityPlan( - javaType, - typeConfiguration - ); + final var determinedPlan = + RegistryHelper.INSTANCE.determineMutabilityPlan( javaTypeClass, typeConfiguration ); final MutabilityPlan mutabilityPlan = determinedPlan != null ? determinedPlan - : (MutabilityPlan) MutableMutabilityPlan.INSTANCE; - - return entity - ? new EntityJavaType<>( javaTypeClass, mutabilityPlan ) - : new JavaTypeBasicAdaptor<>( javaTypeClass, mutabilityPlan ); + : MutableMutabilityPlan.instance(); + return instantiate.apply( javaTypeClass, mutabilityPlan ); } ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/RegistryHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/RegistryHelper.java index 37f18bfd1473..1eda91306014 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/RegistryHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/RegistryHelper.java @@ -12,6 +12,7 @@ import org.hibernate.annotations.Immutable; import org.hibernate.annotations.Mutability; import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.type.descriptor.java.BasicJavaType; import org.hibernate.type.descriptor.java.EnumJavaType; import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan; import org.hibernate.type.descriptor.java.JavaType; @@ -36,27 +37,37 @@ public JavaType createTypeDescriptor( Type javaType, Supplier> fallbackMutabilityPlanResolver, TypeConfiguration typeConfiguration) { - return createTypeDescriptor( - javaType, - (javaTypeClass) -> { - MutabilityPlan mutabilityPlan = determineMutabilityPlan( javaType, typeConfiguration ); - if ( mutabilityPlan == null ) { - mutabilityPlan = fallbackMutabilityPlanResolver.get(); - } - return mutabilityPlan; - } - ); + return createTypeDescriptor( determineJavaTypeClass( javaType ), + javaTypeClass -> mutabilityPlan( javaTypeClass, fallbackMutabilityPlanResolver, typeConfiguration ) ); } - public MutabilityPlan determineMutabilityPlan(Type javaType, TypeConfiguration typeConfiguration) { - final Class javaTypeClass = determineJavaTypeClass( javaType ); + public JavaType createTypeDescriptor( + Class javaType, + Supplier> fallbackMutabilityPlanResolver, + TypeConfiguration typeConfiguration) { + return createTypeDescriptor( javaType, + javaTypeClass -> mutabilityPlan( javaTypeClass, fallbackMutabilityPlanResolver, typeConfiguration ) ); + } + private MutabilityPlan mutabilityPlan( + Class javaTypeClass, + Supplier> fallbackMutabilityPlanResolver, + TypeConfiguration typeConfiguration) { + var mutabilityPlan = determineMutabilityPlan( javaTypeClass, typeConfiguration ); + return mutabilityPlan == null ? fallbackMutabilityPlanResolver.get() : mutabilityPlan; + } + + public MutabilityPlan determineMutabilityPlan(Type javaType, TypeConfiguration typeConfiguration) { + return determineMutabilityPlan( determineJavaTypeClass( javaType ), typeConfiguration ); + } + + public MutabilityPlan determineMutabilityPlan(Class javaTypeClass, TypeConfiguration typeConfiguration) { if ( javaTypeClass.isAnnotationPresent( Immutable.class ) ) { return ImmutableMutabilityPlan.instance(); } if ( javaTypeClass.isAnnotationPresent( Mutability.class ) ) { - final Mutability annotation = javaTypeClass.getAnnotation( Mutability.class ); + final var annotation = javaTypeClass.getAnnotation( Mutability.class ); return typeConfiguration.createMutabilityPlan( annotation.value() ); } @@ -67,29 +78,26 @@ public MutabilityPlan determineMutabilityPlan(Type javaType, TypeConfigur if ( Serializable.class.isAssignableFrom( javaTypeClass ) ) { return (MutabilityPlan) SerializableJavaType.SerializableMutabilityPlan.INSTANCE; } - return null; } - private JavaType createTypeDescriptor( - Type javaType, - Function,MutabilityPlan> mutabilityPlanResolver) { - final Class javaTypeClass = determineJavaTypeClass( javaType ); - + private BasicJavaType createTypeDescriptor( + Class javaTypeClass, + Function, MutabilityPlan> mutabilityPlanResolver) { if ( javaTypeClass.isEnum() ) { // enums are unequivocally immutable //noinspection rawtypes, unchecked return new EnumJavaType( javaTypeClass ); } - final MutabilityPlan plan = mutabilityPlanResolver.apply( javaTypeClass ); + final var plan = mutabilityPlanResolver.apply( javaTypeClass ); if ( Serializable.class.isAssignableFrom( javaTypeClass ) ) { //noinspection rawtypes, unchecked return new SerializableJavaType( javaTypeClass, plan ); } - return new UnknownBasicJavaType<>( javaType, plan ); + return new UnknownBasicJavaType<>( javaTypeClass, plan ); } private Class determineJavaTypeClass(Type javaType) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/internal/ParameterizedTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/type/internal/ParameterizedTypeImpl.java index b72de29aebcc..5d563f87aab2 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/internal/ParameterizedTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/type/internal/ParameterizedTypeImpl.java @@ -7,13 +7,12 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; -import java.util.List; import java.util.Objects; import java.util.StringJoiner; import org.hibernate.models.spi.ParameterizedTypeDetails; -import org.hibernate.models.spi.TypeDetails; -import org.hibernate.models.spi.TypeVariableScope; + +import static org.hibernate.models.spi.TypeDetails.Kind.PARAMETERIZED_TYPE; public class ParameterizedTypeImpl implements ParameterizedType { @@ -28,39 +27,33 @@ public ParameterizedTypeImpl(Type rawType, Type[] substTypeArgs, Type ownerType) } public static ParameterizedTypeImpl from(ParameterizedTypeDetails typeDetails) { - final java.lang.reflect.Type attributeType = typeDetails.determineRawClass().toJavaClass(); - - final List arguments = typeDetails.asParameterizedType().getArguments(); + final var attributeType = typeDetails.determineRawClass().toJavaClass(); + final var arguments = typeDetails.asParameterizedType().getArguments(); final int argumentsSize = arguments.size(); - final java.lang.reflect.Type[] argumentTypes = new java.lang.reflect.Type[argumentsSize]; + final var argumentTypes = new Type[argumentsSize]; for ( int i = 0; i < argumentsSize; i++ ) { - TypeDetails argument = arguments.get( i ); - if ( argument.getTypeKind() == TypeDetails.Kind.PARAMETERIZED_TYPE ) { - argumentTypes[i] = from( argument.asParameterizedType() ); - } - else { - argumentTypes[i] = argument.determineRawClass().toJavaClass(); - } - } - final TypeVariableScope owner = typeDetails.asParameterizedType().getOwner(); - final java.lang.reflect.Type ownerType; - if ( owner != null ) { - ownerType = owner.determineRawClass().toJavaClass(); - } - else { - ownerType = null; + final var argument = arguments.get( i ); + argumentTypes[i] = + argument.getTypeKind() == PARAMETERIZED_TYPE + ? from( argument.asParameterizedType() ) + : argument.determineRawClass().toJavaClass(); } + final var owner = typeDetails.asParameterizedType().getOwner(); + final var ownerType = owner == null ? null : owner.determineRawClass().toJavaClass(); return new ParameterizedTypeImpl( attributeType, argumentTypes, ownerType ); } + @Override public Type[] getActualTypeArguments() { return substTypeArgs; } + @Override public Type getRawType() { return rawType; } + @Override public Type getOwnerType() { return ownerType; } @@ -84,42 +77,37 @@ public int hashCode() { @Override public String toString() { - final StringBuilder sb = new StringBuilder(); + final var typeExpression = new StringBuilder(); if ( ownerType != null ) { - sb.append( ownerType.getTypeName() ); - - sb.append( "$" ); - + typeExpression.append( ownerType.getTypeName() ).append( "$" ); if ( ownerType instanceof ParameterizedType parameterizedType ) { - // Find simple name of nested type by removing the - // shared prefix with owner. - sb.append( - rawType.getTypeName().replace( - parameterizedType.getRawType().getTypeName() + "$", - "" - ) - ); + // Find the simple name of the nested type by + // removing the shared prefix with the outer type. + final int prefixLength = + parameterizedType.getRawType().getTypeName().length() + + 1; // account for the '$' separator + typeExpression.append( rawType.getTypeName().substring( prefixLength ) ); } else if ( rawType instanceof Class clazz ) { - sb.append( clazz.getSimpleName() ); + typeExpression.append( clazz.getSimpleName() ); } else { - sb.append( rawType.getTypeName() ); + typeExpression.append( rawType.getTypeName() ); } } else { - sb.append( rawType.getTypeName() ); + typeExpression.append( rawType.getTypeName() ); } if ( substTypeArgs != null ) { - final StringJoiner sj = new StringJoiner( ", ", "<", ">" ); - sj.setEmptyValue( "" ); - for ( Type t : substTypeArgs ) { - sj.add( t.getTypeName() ); + final var argList = new StringJoiner( ", ", "<", ">" ); + argList.setEmptyValue( "" ); + for ( var type : substTypeArgs ) { + argList.add( type.getTypeName() ); } - sb.append( sj ); + typeExpression.append( argList ); } - return sb.toString(); + return typeExpression.toString(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java b/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java index 81d57989d18c..daea60d36aed 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java +++ b/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java @@ -83,6 +83,7 @@ import static org.hibernate.id.uuid.LocalObjectUuidHelper.generateLocalObjectUuid; import static org.hibernate.internal.util.NullnessUtil.castNonNull; +import static org.hibernate.internal.util.type.PrimitiveWrappers.canonicalize; import static org.hibernate.query.sqm.internal.TypecheckUtil.isNumberArray; /** @@ -760,14 +761,41 @@ private static boolean matchesJavaType(SqmExpressible type, Class javaType private final ConcurrentHashMap> basicTypeByJavaType = new ConcurrentHashMap<>(); + private static BasicType checkExisting(Class javaClass, BasicType existing) { + if ( !isCompatible( javaClass, existing.getJavaType() ) ) { + throw new IllegalStateException( "Type registration was corrupted for: " + javaClass.getName() ); + } + @SuppressWarnings("unchecked") // safe, we just checked + final var basicType = (BasicType) existing; + return basicType; + } + + private static boolean isCompatible(Class javaClass, Class existing) { + return canonicalize( javaClass ).isAssignableFrom( existing ); + } + + @Deprecated(since = "7.2", forRemoval = true) // no longer used public @Nullable BasicType getBasicTypeForGenericJavaType(Class javaType, Type... typeArguments) { //noinspection unchecked - return (BasicType) getBasicTypeForJavaType( new ParameterizedTypeImpl( javaType, typeArguments, null ) ); + return (BasicType) + getBasicTypeForJavaType( new ParameterizedTypeImpl( javaType, typeArguments, null ) ); } - public @Nullable BasicType getBasicTypeForJavaType(Class javaType) { - //noinspection unchecked - return (BasicType) getBasicTypeForJavaType( (Type) javaType ); + public @Nullable BasicType getBasicTypeForJavaType(Class javaClass) { + final var existing = basicTypeByJavaType.get( javaClass ); + if ( existing != null ) { + return checkExisting( javaClass, existing ); + } + else { + final var registeredType = basicTypeRegistry.getRegisteredType( javaClass ); + if ( registeredType != null ) { + basicTypeByJavaType.put( javaClass, registeredType ); + return registeredType; + } + else { + return null; + } + } } public @Nullable BasicType getBasicTypeForJavaType(Type javaType) { @@ -787,65 +815,64 @@ private static boolean matchesJavaType(SqmExpressible type, Class javaType } } - public BasicType standardBasicTypeForJavaType(Class javaType) { - if ( javaType == null ) { - return null; - } - else { - return standardBasicTypeForJavaType( javaType, - javaTypeDescriptor -> new BasicTypeImpl<>( javaTypeDescriptor, - javaTypeDescriptor.getRecommendedJdbcType( getCurrentBaseSqlTypeIndicators() ) ) ); - } + public BasicType standardBasicTypeForJavaType(Class javaClass) { + return javaClass == null ? null + : standardBasicTypeForJavaType( javaClass, + descriptor -> new BasicTypeImpl<>( descriptor, + descriptor.getRecommendedJdbcType( + getCurrentBaseSqlTypeIndicators() ) ) ); } public BasicType standardBasicTypeForJavaType(Type javaType) { - if ( javaType == null ) { - return null; - } - else { - return standardBasicTypeForJavaType( javaType, - javaTypeDescriptor -> new BasicTypeImpl<>( javaTypeDescriptor, - javaTypeDescriptor.getRecommendedJdbcType( getCurrentBaseSqlTypeIndicators() ) ) ); - } + return javaType == null ? null + : standardBasicTypeForJavaType( javaType, + descriptor -> new BasicTypeImpl<>( descriptor, + descriptor.getRecommendedJdbcType( + getCurrentBaseSqlTypeIndicators() ) ) ); } @Deprecated(since = "7.2", forRemoval = true) // Can be private public BasicType standardBasicTypeForJavaType( - Class javaType, + Class javaClass, Function, BasicType> creator) { - if ( javaType == null ) { + if ( javaClass == null ) { return null; } - //noinspection unchecked - return (BasicType) basicTypeByJavaType.computeIfAbsent( - javaType, - jt -> { - // See if one exists in the BasicTypeRegistry and use that one if so - final var registeredType = basicTypeRegistry.getRegisteredType( javaType ); - return registeredType != null - ? registeredType - : creator.apply( javaTypeRegistry.getDescriptor( javaType ) ); - } - ); + var existing = basicTypeByJavaType.get( javaClass ); + if ( existing != null ) { + return checkExisting( javaClass, existing ); + } + else { + // See if one exists in the BasicTypeRegistry and use that one if so + final var registeredType = + basicTypeRegistry.getRegisteredType( javaClass ); + return registeredType == null + ? creator.apply( javaTypeRegistry.getDescriptor( javaClass ) ) + : registeredType; + } } - @Deprecated(since = "7.2", forRemoval = true) // Due to weird signature + @Deprecated(since = "7.2", forRemoval = true) // Due to weird signature and unchecked cast public BasicType standardBasicTypeForJavaType( Type javaType, Function, BasicType> creator) { if ( javaType == null ) { return null; } - return basicTypeByJavaType.computeIfAbsent( - javaType, - jt -> { - // See if one exists in the BasicTypeRegistry and use that one if so - final var registeredType = basicTypeRegistry.getRegisteredType( javaType ); - return registeredType != null - ? registeredType - : creator.apply( javaTypeRegistry.getDescriptor( javaType ) ); - } - ); + var existing = basicTypeByJavaType.get( javaType ); + if ( existing != null ) { + return existing; + } + else { + // See if one exists in the BasicTypeRegistry and use that one if so + final var registeredType = + basicTypeRegistry.getRegisteredType( javaType ); + //noinspection unchecked + return registeredType == null + ? creator.apply( (JavaType) // UNCHECKED CAST + javaTypeRegistry.getDescriptor( javaType ) ) + : registeredType; + } } @SuppressWarnings("deprecation") From 58c588d4b225787ceecc6424afc0a44597d13c35 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 8 Dec 2025 11:58:54 +0100 Subject: [PATCH 052/181] use 'var' in DynamicInstantiation stuff --- .../internal/DynamicInstantiation.java | 18 +++++++------- .../DynamicInstantiationArgument.java | 7 +++--- ...InstantiationAssemblerConstructorImpl.java | 6 ++--- ...icInstantiationAssemblerInjectionImpl.java | 22 +++++++---------- ...DynamicInstantiationAssemblerListImpl.java | 6 ++--- .../DynamicInstantiationAssemblerMapImpl.java | 14 ++++------- .../DynamicInstantiationResultImpl.java | 24 +++++++------------ .../internal/InstantiationHelper.java | 9 ++++--- 8 files changed, 45 insertions(+), 61 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiation.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiation.java index 6ebfd95a82f0..6ca2cb334c34 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiation.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiation.java @@ -7,7 +7,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import org.hibernate.query.sqm.DynamicInstantiationNature; import org.hibernate.query.sqm.sql.ConversionException; @@ -18,17 +17,19 @@ import org.jboss.logging.Logger; +import static java.util.stream.Collectors.toList; + /** * Represents a dynamic-instantiation (from an SQM query) as a DomainResultProducer * * @author Steve Ebersole */ -public class DynamicInstantiation implements DomainResultProducer { +public class DynamicInstantiation implements DomainResultProducer { private static final Logger LOG = Logger.getLogger( DynamicInstantiation.class ); private final DynamicInstantiationNature nature; private final JavaType targetJavaType; - private List arguments; + private List> arguments; private boolean argumentAdditionsComplete = false; @@ -47,7 +48,7 @@ public JavaType getTargetJavaType() { return targetJavaType; } - public void addArgument(String alias, DomainResultProducer argumentResultProducer, DomainResultCreationState creationState) { + public void addArgument(String alias, DomainResultProducer argumentResultProducer) { if ( argumentAdditionsComplete ) { throw new ConversionException( "Unexpected call to DynamicInstantiation#addAgument after previously complete" ); } @@ -82,7 +83,7 @@ public void complete() { argumentAdditionsComplete = true; } - public List getArguments() { + public List> getArguments() { return arguments; } @@ -92,17 +93,16 @@ public String toString() { } @Override - public DomainResult createDomainResult( + public DomainResult createDomainResult( String resultVariable, DomainResultCreationState creationState) { - //noinspection unchecked - return new DynamicInstantiationResultImpl( + return new DynamicInstantiationResultImpl<>( resultVariable, getNature(), getTargetJavaType(), getArguments().stream() .map( argument -> argument.buildArgumentDomainResult( creationState ) ) - .collect( Collectors.toList() ) + .collect( toList() ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationArgument.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationArgument.java index 2d1f5d5438ec..cb5677668f48 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationArgument.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationArgument.java @@ -6,7 +6,6 @@ import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter; import org.hibernate.query.sqm.sql.internal.DomainResultProducer; -import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.results.graph.DomainResultCreationState; /** @@ -26,9 +25,9 @@ public String getAlias() { } public ArgumentDomainResult buildArgumentDomainResult(DomainResultCreationState creationState) { - final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlAstCreationState() - .getCurrentProcessingState() - .getSqlExpressionResolver(); + final var sqlExpressionResolver = + creationState.getSqlAstCreationState().getCurrentProcessingState() + .getSqlExpressionResolver(); if ( sqlExpressionResolver instanceof BaseSqmToSqlAstConverter.SqmAliasedNodeCollector ) { if ( !( argumentResultProducer instanceof DynamicInstantiation ) ) { ( (BaseSqmToSqlAstConverter.SqmAliasedNodeCollector) sqlExpressionResolver ).next(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerConstructorImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerConstructorImpl.java index 99a90e91f6a1..7e411f7429e7 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerConstructorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerConstructorImpl.java @@ -40,7 +40,7 @@ public JavaType getAssembledJavaType() { @Override public R assemble(RowProcessingState rowProcessingState) { final int numberOfArgs = argumentReaders.size(); - Object[] args = new Object[ numberOfArgs ]; + final var args = new Object[ numberOfArgs ]; for ( int i = 0; i < numberOfArgs; i++ ) { args[i] = argumentReaders.get( i ).assemble( rowProcessingState ); } @@ -60,14 +60,14 @@ public R assemble(RowProcessingState rowProcessingState) { @Override public void resolveState(RowProcessingState rowProcessingState) { - for ( ArgumentReader argumentReader : argumentReaders ) { + for ( var argumentReader : argumentReaders ) { argumentReader.resolveState( rowProcessingState ); } } @Override public void forEachResultAssembler(BiConsumer, X> consumer, X arg) { - for ( ArgumentReader argumentReader : argumentReaders ) { + for ( var argumentReader : argumentReaders ) { argumentReader.forEachResultAssembler( consumer, arg ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerInjectionImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerInjectionImpl.java index cd842c71c7b3..d3b298e0b9b0 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerInjectionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerInjectionImpl.java @@ -5,11 +5,7 @@ package org.hibernate.sql.results.graph.instantiation.internal; import java.beans.BeanInfo; -import java.beans.PropertyDescriptor; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.function.BiConsumer; @@ -35,7 +31,7 @@ public DynamicInstantiationAssemblerInjectionImpl( JavaType target, List> argumentReaders) { this.target = target; - final Class targetJavaType = target.getJavaTypeClass(); + final var targetJavaType = target.getJavaTypeClass(); final List beanInjections = new ArrayList<>( argumentReaders.size() ); BeanInfoHelper.visitBeanInfo( targetJavaType, @@ -58,20 +54,20 @@ private DynamicInstantiationAssemblerInjectionImpl(List beanInjec } private static BeanInjection injection(BeanInfo beanInfo, ArgumentReader argument, Class targetJavaType) { - final Class argType = argument.getAssembledJavaType().getJavaTypeClass(); + final var argType = argument.getAssembledJavaType().getJavaTypeClass(); final String alias = argument.getAlias(); // see if we can find a property with the given name... - for ( PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors() ) { + for ( var propertyDescriptor : beanInfo.getPropertyDescriptors() ) { if ( propertyMatches( alias, argType, propertyDescriptor ) ) { - final Method setter = propertyDescriptor.getWriteMethod(); + final var setter = propertyDescriptor.getWriteMethod(); setter.setAccessible(true); return new BeanInjection( new BeanInjectorSetter<>( setter ), argument ); } } // see if we can find a Field with the given name... - final Field field = findField( targetJavaType, alias, argType ); + final var field = findField( targetJavaType, alias, argType ); if ( field != null ) { return new BeanInjection( new BeanInjectorField<>( field ), argument ); } @@ -92,7 +88,7 @@ public JavaType getAssembledJavaType() { public T assemble(RowProcessingState rowProcessingState) { final T result; try { - final Constructor constructor = target.getJavaTypeClass().getDeclaredConstructor(); + final var constructor = target.getJavaTypeClass().getDeclaredConstructor(); constructor.setAccessible( true ); result = constructor.newInstance(); } @@ -101,7 +97,7 @@ public T assemble(RowProcessingState rowProcessingState) { throw new InstantiationException( "Error instantiating class '" + target.getTypeName() + "' using default constructor: " + e.getMessage(), e ); } - for ( BeanInjection beanInjection : beanInjections ) { + for ( var beanInjection : beanInjections ) { final Object assembled = beanInjection.getValueAssembler().assemble( rowProcessingState ); beanInjection.getBeanInjector().inject( result, assembled ); } @@ -110,14 +106,14 @@ public T assemble(RowProcessingState rowProcessingState) { @Override public void resolveState(RowProcessingState rowProcessingState) { - for ( BeanInjection beanInjection : beanInjections ) { + for ( var beanInjection : beanInjections ) { beanInjection.getValueAssembler().resolveState( rowProcessingState ); } } @Override public void forEachResultAssembler(BiConsumer, X> consumer, X arg) { - for ( BeanInjection beanInjection : beanInjections ) { + for ( var beanInjection : beanInjections ) { beanInjection.getValueAssembler().forEachResultAssembler( consumer, arg ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerListImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerListImpl.java index ff44cbf5fb12..da4ad30545a2 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerListImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerListImpl.java @@ -41,7 +41,7 @@ public JavaType> getAssembledJavaType() { public List assemble( RowProcessingState rowProcessingState) { final ArrayList result = new ArrayList<>(); - for ( ArgumentReader argumentReader : argumentReaders ) { + for ( var argumentReader : argumentReaders ) { result.add( argumentReader.assemble( rowProcessingState ) ); } return result; @@ -49,14 +49,14 @@ public List assemble( @Override public void resolveState(RowProcessingState rowProcessingState) { - for ( ArgumentReader argumentReader : argumentReaders ) { + for ( var argumentReader : argumentReaders ) { argumentReader.resolveState( rowProcessingState ); } } @Override public void forEachResultAssembler(BiConsumer, X> consumer, X arg) { - for ( ArgumentReader argumentReader : argumentReaders ) { + for ( var argumentReader : argumentReaders ) { argumentReader.forEachResultAssembler( consumer, arg ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerMapImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerMapImpl.java index e6c5b1a34149..7252811a6764 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerMapImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationAssemblerMapImpl.java @@ -33,7 +33,7 @@ public DynamicInstantiationAssemblerMapImpl( this.argumentReaders = argumentReaders; final Set aliases = new HashSet<>(); - for ( ArgumentReader argumentReader : argumentReaders ) { + for ( var argumentReader : argumentReaders ) { if ( argumentReader.getAlias() == null ) { throw new IllegalStateException( "alias for Map dynamic instantiation argument cannot be null" ); } @@ -60,20 +60,16 @@ public JavaType> getAssembledJavaType() { public Map assemble( RowProcessingState rowProcessingState) { final HashMap result = new HashMap<>(); - - for ( ArgumentReader argumentReader : argumentReaders ) { - result.put( - argumentReader.getAlias(), - argumentReader.assemble( rowProcessingState ) - ); + for ( var argumentReader : argumentReaders ) { + result.put( argumentReader.getAlias(), + argumentReader.assemble( rowProcessingState ) ); } - return result; } @Override public void resolveState(RowProcessingState rowProcessingState) { - for ( ArgumentReader argumentReader : argumentReaders ) { + for ( var argumentReader : argumentReaders ) { argumentReader.resolveState( rowProcessingState ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationResultImpl.java index 3bee263d66d4..58ac3dd808e1 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationResultImpl.java @@ -4,7 +4,6 @@ */ package org.hibernate.sql.results.graph.instantiation.internal; -import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.BitSet; import java.util.HashSet; @@ -19,7 +18,6 @@ import org.hibernate.sql.results.graph.InitializerParent; import org.hibernate.sql.results.graph.instantiation.DynamicInstantiationResult; import org.hibernate.type.descriptor.java.JavaType; -import org.hibernate.type.spi.TypeConfiguration; import org.jboss.logging.Logger; @@ -63,7 +61,7 @@ public String getResultVariable() { public boolean containsAnyNonScalarResults() { //noinspection ForLoopReplaceableByForEach for ( int i = 0; i < argumentResults.size(); i++ ) { - final ArgumentDomainResult argumentResult = argumentResults.get( i ); + final var argumentResult = argumentResults.get( i ); if ( argumentResult.containsAnyNonScalarResults() ) { return true; } @@ -74,7 +72,7 @@ public boolean containsAnyNonScalarResults() { @Override public void collectValueIndexesToCache(BitSet valueIndexes) { - for ( ArgumentDomainResult argumentResult : argumentResults ) { + for ( var argumentResult : argumentResults ) { argumentResult.collectValueIndexesToCache( valueIndexes ); } } @@ -88,7 +86,7 @@ public DomainResultAssembler createResultAssembler(InitializerParent paren final List> argumentReaders = new ArrayList<>(); if ( argumentResults != null ) { - for ( ArgumentDomainResult argumentResult : argumentResults ) { + for ( var argumentResult : argumentResults ) { final String argumentAlias = argumentResult.getResultVariable(); if ( argumentAlias == null ) { areAllArgumentsAliased = false; @@ -155,19 +153,15 @@ private DomainResultAssembler assembler( List duplicatedAliases, List> argumentReaders, AssemblerCreationState creationState) { - final List> argumentTypes = + // find a constructor matching argument types + final var constructor = findMatchingConstructor( + javaType.getJavaTypeClass(), argumentReaders.stream() - .map(reader -> reader.getAssembledJavaType().getJavaTypeClass()) - .collect(toList()); - final TypeConfiguration typeConfiguration = + .map( reader -> reader.getAssembledJavaType().getJavaTypeClass() ) + .collect( toList() ), creationState.getSqlAstCreationContext() .getMappingMetamodel() - .getTypeConfiguration(); - // find a constructor matching argument types - final Constructor constructor = findMatchingConstructor( - javaType.getJavaTypeClass(), - argumentTypes, - typeConfiguration + .getTypeConfiguration() ); if ( constructor != null ) { constructor.setAccessible( true ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/InstantiationHelper.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/InstantiationHelper.java index faa0bf5813c2..d41a125b6473 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/InstantiationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/InstantiationHelper.java @@ -13,7 +13,6 @@ import java.beans.PropertyDescriptor; import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.lang.reflect.Type; import java.util.List; import static org.hibernate.query.sqm.tree.expression.Compatibility.areAssignmentCompatible; @@ -47,7 +46,7 @@ public static boolean isInjectionCompatible(Class targetJavaType, List targetJavaType, BeanInfo beanInfo, String alias, Class argType) { - for ( PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors() ) { + for ( var propertyDescriptor : beanInfo.getPropertyDescriptors() ) { if ( propertyMatches( alias, argType, propertyDescriptor ) ) { return true; } @@ -63,7 +62,7 @@ public static boolean isConstructorCompatible(Class javaClass, List> Class type, List> argumentTypes, TypeConfiguration typeConfiguration) { - for ( final Constructor constructor : type.getDeclaredConstructors() ) { + for ( var constructor : type.getDeclaredConstructors() ) { if ( isConstructorCompatible( constructor, argumentTypes, typeConfiguration ) ) { //noinspection unchecked return (Constructor) constructor; @@ -79,7 +78,7 @@ public static boolean isConstructorCompatible( final var genericParameterTypes = constructor.getGenericParameterTypes(); if ( genericParameterTypes.length == argumentTypes.size() ) { for (int i = 0; i < argumentTypes.size(); i++ ) { - final Type parameterType = genericParameterTypes[i]; + final var parameterType = genericParameterTypes[i]; final var argumentType = argumentTypes.get( i ); final var type = parameterType instanceof Class classParameter @@ -107,7 +106,7 @@ public static boolean isConstructorCompatible( static Field findField(Class declaringClass, String name, Class javaType) { try { - final Field field = declaringClass.getDeclaredField( name ); + final var field = declaringClass.getDeclaredField( name ); // field should never be null if ( areAssignmentCompatible( field.getType(), javaType ) ) { field.setAccessible( true ); From 643b02b4655f71167217c0afe43092facb3303e3 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 8 Dec 2025 11:59:11 +0100 Subject: [PATCH 053/181] clean up warnings in a test --- .../discriminator/DiscriminatorTest.java | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/discriminator/DiscriminatorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/discriminator/DiscriminatorTest.java index 75e32b28091c..f6c228afe18c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/discriminator/DiscriminatorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/discriminator/DiscriminatorTest.java @@ -5,10 +5,6 @@ package org.hibernate.orm.test.mapping.inheritance.discriminator; import java.math.BigDecimal; -import java.util.List; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Root; import org.hibernate.Hibernate; import org.hibernate.proxy.HibernateProxy; @@ -24,6 +20,7 @@ import static org.hamcrest.Matchers.instanceOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; @@ -38,6 +35,7 @@ xmlMappings = "org/hibernate/orm/test/mapping/inheritance/discriminator/Person.hbm.xml" ) @SessionFactory +@SuppressWarnings("deprecation") public class DiscriminatorTest { @AfterEach @@ -87,8 +85,9 @@ public void testDiscriminatorSubclass(SessionFactoryScope scope) { assertThat( s.createQuery( "from Person p where p.class = Customer" ).list().size(), is( 1 ) ); s.clear(); - List customers = s.createQuery( "from Customer c left join fetch c.salesperson" ).list(); - for ( Customer c : customers ) { + var customers = s.createQuery( "from Customer c left join fetch c.salesperson" ).list(); + for ( var customer : customers ) { + var c = (Customer) customer; assertTrue( Hibernate.isInitialized( c.getSalesperson() ) ); assertThat( c.getSalesperson().getName(), is( "Mark" ) ); } @@ -96,7 +95,8 @@ public void testDiscriminatorSubclass(SessionFactoryScope scope) { s.clear(); customers = s.createQuery( "from Customer" ).list(); - for ( Customer c : customers ) { + for ( var customer : customers ) { + var c = (Customer) customer; assertFalse( Hibernate.isInitialized( c.getSalesperson() ) ); assertThat( c.getSalesperson().getName(), is( "Mark" ) ); } @@ -164,22 +164,22 @@ public void testQuerySubclassAttribute(SessionFactoryScope scope) { q.setSalary( new BigDecimal( 1000 ) ); s.persist( q ); - List result = s.createQuery( "from Person where salary > 100" ).list(); - assertEquals( result.size(), 1 ); + var result = s.createQuery( "from Person where salary > 100" ).list(); + assertEquals( 1, result.size() ); assertSame( result.get( 0 ), q ); result = s.createQuery( "from Person where salary > 100 or name like 'E%'" ).list(); - assertEquals( result.size(), 2 ); + assertEquals( 2, result.size() ); - CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder(); - CriteriaQuery criteria = criteriaBuilder.createQuery( Person.class ); - Root root = criteria.from( Person.class ); + var criteriaBuilder = s.getCriteriaBuilder(); + var criteria = criteriaBuilder.createQuery( Person.class ); + var root = criteria.from( Person.class ); criteria.where( criteriaBuilder.gt( criteriaBuilder.treat( root, Employee.class ).get( "salary" ), new BigDecimal( 100 ) ) ); result = s.createQuery( criteria ).list(); // result = s.createCriteria(Person.class) // .add( Property.forName( "salary").gt( new BigDecimal( 100) ) ) // .list(); - assertEquals( result.size(), 1 ); + assertEquals( 1, result.size() ); assertSame( result.get( 0 ), q ); //TODO: make this work: @@ -210,14 +210,14 @@ public void testLoadSuperclassProxyPolymorphicAccess(SessionFactoryScope scope) s -> { // load the superclass proxy. Person pLoad = s.getReference( Person.class, e.getId() ); - assertTrue( pLoad instanceof HibernateProxy ); + assertInstanceOf( HibernateProxy.class, pLoad ); Person pGet = s.get( Person.class, e.getId() ); Person pQuery = (Person) s.createQuery( "from Person where id = :id" ) .setParameter( "id", e.getId() ) .uniqueResult(); - CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder(); - CriteriaQuery criteria = criteriaBuilder.createQuery( Person.class ); - Root root = criteria.from( Person.class ); + var criteriaBuilder = s.getCriteriaBuilder(); + var criteria = criteriaBuilder.createQuery( Person.class ); + var root = criteria.from( Person.class ); criteria.where( criteriaBuilder.equal( root.get( "id" ), e.getId() ) ); Person pCriteria = s.createQuery( criteria ).uniqueResult(); // Person pCriteria = ( Person ) s.createCriteria( Person.class ) @@ -237,14 +237,14 @@ public void testLoadSuperclassProxyPolymorphicAccess(SessionFactoryScope scope) s -> { // load the superclass proxy. Person pLoad = s.getReference( Person.class, e.getId() ); - assertTrue( pLoad instanceof HibernateProxy ); + assertInstanceOf( HibernateProxy.class, pLoad ); Person pGet = s.get( Person.class, e.getId() ); Person pQuery = (Person) s.createQuery( "from Person where id = :id" ) .setParameter( "id", e.getId() ) .uniqueResult(); - CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder(); - CriteriaQuery criteria = criteriaBuilder.createQuery( Employee.class ); - Root root = criteria.from( Employee.class ); + var criteriaBuilder = s.getCriteriaBuilder(); + var criteria = criteriaBuilder.createQuery( Employee.class ); + var root = criteria.from( Employee.class ); criteria.where( criteriaBuilder.equal( root.get( "id" ), e.getId() ) ); Employee pCriteria = s.createQuery( criteria ).uniqueResult(); // Person pCriteria = ( Person ) s.createCriteria( Person.class ) @@ -281,16 +281,16 @@ public void testLoadSuperclassProxyEvictPolymorphicAccess(SessionFactoryScope sc s -> { // load the superclass proxy. Person pLoad = s.getReference( Person.class, e.getId() ); - assertTrue( pLoad instanceof HibernateProxy ); + assertInstanceOf( HibernateProxy.class, pLoad ); // evict the proxy s.evict( pLoad ); Employee pGet = (Employee) s.get( Person.class, e.getId() ); Employee pQuery = (Employee) s.createQuery( "from Person where id = :id" ) .setParameter( "id", e.getId() ) .uniqueResult(); - CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder(); - CriteriaQuery criteria = criteriaBuilder.createQuery( Person.class ); - Root root = criteria.from( Person.class ); + var criteriaBuilder = s.getCriteriaBuilder(); + var criteria = criteriaBuilder.createQuery( Person.class ); + var root = criteria.from( Person.class ); criteria.where( criteriaBuilder.equal( root.get( "id" ), e.getId() ) ); Employee pCriteria = (Employee) s.createQuery( criteria ).uniqueResult(); From bbf0c5fbfa8b5c0139709c048d63d76634f58002 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 8 Dec 2025 12:01:26 +0100 Subject: [PATCH 054/181] more work on coercion, casting, and isInstance() --- .../internal/DefaultLoadEventListener.java | 2 +- .../internal/SimpleNaturalIdMapping.java | 2 +- .../sqm/sql/BaseSqmToSqlAstConverter.java | 15 +++----- .../graph/basic/BasicResultAssembler.java | 2 +- .../internal/EntityInitializerImpl.java | 13 ++++--- .../type/descriptor/java/JavaType.java | 38 ++++++++++++++++++- .../descriptor/java/SerializableJavaType.java | 7 +--- 7 files changed, 53 insertions(+), 26 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java index 12e95883e97a..01354e2f124a 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java @@ -114,7 +114,7 @@ private boolean handleIdType(EntityPersister persister, LoadEvent event, LoadTyp parentIdTargetIdMapping instanceof CompositeIdentifierMapping compositeMapping ? compositeMapping.getMappedIdEmbeddableTypeDescriptor() : parentIdTargetIdMapping.getMappedType(); - if ( parentIdType.getMappedJavaType().getJavaTypeClass().isInstance( event.getEntityId() ) ) { + if ( parentIdType.getMappedJavaType().isInstance( event.getEntityId() ) ) { // yep that's what we have... loadByDerivedIdentitySimplePkValue( event, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleNaturalIdMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleNaturalIdMapping.java index f8910b60e35d..f647a84a9aa4 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleNaturalIdMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleNaturalIdMapping.java @@ -115,7 +115,7 @@ public void validateInternalForm(Object naturalIdValue) { } } - if ( !getJavaType().getJavaTypeClass().isInstance( naturalIdValue ) ) { + if ( !getJavaType().isInstance( naturalIdValue ) ) { throw new IllegalArgumentException( String.format( Locale.ROOT, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index e540e6d58bc7..9f98cd259fca 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -1650,22 +1650,17 @@ public SelectStatement visitSelectStatement(SqmSelectStatement statement) { @Override public DynamicInstantiation visitDynamicInstantiation(SqmDynamicInstantiation sqmDynamicInstantiation) { final var instantiationTarget = sqmDynamicInstantiation.getInstantiationTarget(); - final var instantiationNature = instantiationTarget.getNature(); - final var targetTypeDescriptor = interpretInstantiationTarget( instantiationTarget ); - - final var dynamicInstantiation = new DynamicInstantiation<>( instantiationNature, targetTypeDescriptor ); - + final var dynamicInstantiation = + new DynamicInstantiation<>( instantiationTarget.getNature(), + interpretInstantiationTarget( instantiationTarget ) ); for ( var sqmArgument : sqmDynamicInstantiation.getArguments() ) { if ( sqmArgument.getSelectableNode() instanceof SqmPath sqmPath ) { prepareForSelection( sqmPath ); } - final var argumentResultProducer = (DomainResultProducer) sqmArgument.accept( this ); - - dynamicInstantiation.addArgument( sqmArgument.getAlias(), argumentResultProducer, this ); + dynamicInstantiation.addArgument( sqmArgument.getAlias(), + (DomainResultProducer) sqmArgument.accept( this ) ); } - dynamicInstantiation.complete(); - return dynamicInstantiation; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/BasicResultAssembler.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/BasicResultAssembler.java index c25dee2ac2bf..6a6937281095 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/BasicResultAssembler.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/BasicResultAssembler.java @@ -60,7 +60,7 @@ public J assemble( if ( valueConverter != null ) { if ( jdbcValue != null ) { // the raw value type should be the converter's relational-JTD - if ( ! valueConverter.getRelationalJavaType().getJavaTypeClass().isInstance( jdbcValue ) ) { + if ( ! valueConverter.getRelationalJavaType().isInstance( jdbcValue ) ) { throw new HibernateException( String.format( Locale.ROOT, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java index 068253ec19a6..352262529287 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java @@ -1154,6 +1154,8 @@ private void resolveEntity(EntityInitializerData data, Object proxy) { public void resolveInstance(EntityInitializerData data) { if ( data.getState() == State.KEY_RESOLVED ) { final var rowProcessingState = data.getRowProcessingState(); + final var session = rowProcessingState.getSession(); + final var persistenceContext = session.getPersistenceContextInternal(); data.setState( State.RESOLVED ); if ( data.entityKey == null ) { assert identifierAssembler != null; @@ -1165,7 +1167,7 @@ public void resolveInstance(EntityInitializerData data) { resolveEntityKey( data, id ); } data.entityHolder = - rowProcessingState.getSession().getPersistenceContextInternal() + persistenceContext .claimEntityHolderIfPossible( data.entityKey, null, @@ -1179,7 +1181,6 @@ public void resolveInstance(EntityInitializerData data) { else { resolveEntityInstance1( data ); if ( data.uniqueKeyAttributePath != null ) { - final var session = rowProcessingState.getSession(); final var concreteDescriptor = getConcreteDescriptor( data ); final var entityUniqueKey = new EntityUniqueKey( concreteDescriptor.getEntityName(), @@ -1188,8 +1189,7 @@ public void resolveInstance(EntityInitializerData data) { data.uniqueKeyPropertyTypes[concreteDescriptor.getSubclassId()], session.getFactory() ); - session.getPersistenceContextInternal() - .addEntity( entityUniqueKey, data.getInstance() ); + persistenceContext.addEntity( entityUniqueKey, data.getInstance() ); } } @@ -1346,7 +1346,10 @@ protected void upgradeLockMode(EntityInitializerData data) { protected boolean isProxyInstance(Object proxy) { return proxy != null && ( proxy instanceof MapProxy - || entityDescriptor.getJavaType().getJavaTypeClass().isInstance( proxy ) ); + // do NOT use JavaType.isInstance() here; we're testing if the + // proxy itself is an instance of the given entity type, not if + // the underlying entity implementation is an instance + || entityDescriptor.getJavaType().getJavaTypeClass().isInstance( proxy ) ); } private boolean isExistingEntityInitialized(Object existingEntity) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java index 93950012891a..594b738d09e1 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java @@ -100,11 +100,27 @@ default String getTypeName() { * but some descriptors need specialized semantics, for example, the descriptors for * {@link JdbcDateJavaType java.sql.Date}, {@link JdbcTimeJavaType java.sql.Time}, and * {@link JdbcTimestampJavaType java.sql.Timestamp}. + *

+ * For {@link org.hibernate.type.descriptor.java.spi.EntityJavaType}, this method + * handles proxies in a semantically correct way, by checking the entity instance + * underlying the proxy object. */ default boolean isInstance(Object value) { return getJavaTypeClass().isInstance( value ); } + /** + * Apply a simple type cast to the given value, without attempting any sort of + * {@linkplain #coerce(Object) coercion} or {@linkplain #wrap wrapping}. This + * method is provided as a convenient way to avoid an unchecked cast to a type + * variable. Use {@code javaType.cast(value)} instead of {@code (T) value} + * wherever possible. + *

+ * Usually just {@link #getJavaTypeClass() getJavaTypeClass().}{@link Class#cast cast(value)}, + * but overridden in some cases as an "optimization". This optimization is + * almost certainly unnecessary, and might even indeed be harmful, since + * {@code Class.cast()} is an intrinsic. + */ default T cast(Object value) { return getJavaTypeClass().cast( value ); } @@ -322,10 +338,28 @@ interface CoercionContext { /** * Coerce the given value to this type, if possible. + *

+ * This method differs from {@link #wrap wrap()} in that it allows + * simple, basic, implicit type conversions, and does not require + * {@link WrapperOptions}. The {@code wrap()} method may be thought + * of as offering explicitly requested type conversions driven by a + * choice of {@link JdbcType}. + *

+ * An implementation of this method reports failure in one of two + * ways, by: + *

    + *
  • throwing {@link CoercionException}, or + *
  • simply returning the given uncoerced value. + *
+ *

+ * Therefore, this method is declared to return {@link Object}. + * In case immediate coercion is required, the following idiom + * may be used: + *

javaType.cast(javaType.coerce(value))
* * @param value The value to coerce - * @return The coerced value, or the given value - * if no coercion was possible + * @return The coerced value, or the given value if no coercion was + * possible * @throws CoercionException if coercion fails */ @Incubating diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/SerializableJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/SerializableJavaType.java index 39544887a8ec..8d8ee131d79c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/SerializableJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/SerializableJavaType.java @@ -58,11 +58,6 @@ private static MutabilityPlan createMutabilityPlan(Class type) { : (MutabilityPlan) SerializableMutabilityPlan.INSTANCE; } - @Override - public boolean isInstance(Object value) { - return value instanceof Serializable; - } - @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { final int typeCode = indicators.isLob() ? Types.BLOB : Types.VARBINARY; @@ -137,7 +132,7 @@ else if (value instanceof Blob blob) { throw new HibernateException( e ); } } - else if ( getJavaTypeClass().isInstance( value ) ) { + else if ( isInstance( value ) ) { return cast( value ); } throw unknownWrap( value.getClass() ); From 9f12e2cfb7aef273534c840a30579ffa3594ae22 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 8 Dec 2025 13:01:41 +0100 Subject: [PATCH 055/181] clean up lost of calls to unsafe JavaTypeRegistry.getDescriptor() method --- .../dialect/CockroachLegacyDialect.java | 2 +- .../community/dialect/DB2LegacyDialect.java | 2 +- .../community/dialect/DerbyDialect.java | 2 +- .../community/dialect/DerbyLegacyDialect.java | 2 +- .../community/dialect/GaussDBDialect.java | 2 +- .../community/dialect/InformixDialect.java | 2 +- .../community/dialect/MySQLLegacyDialect.java | 2 +- .../dialect/OracleLegacyDialect.java | 4 +-- .../dialect/PostgreSQLLegacyDialect.java | 2 +- .../community/dialect/SingleStoreDialect.java | 2 +- .../dialect/SybaseLegacyDialect.java | 4 +-- .../boot/model/internal/BasicValueBinder.java | 2 +- .../dialect/function/array/DdlTypeHelper.java | 6 ++-- .../org/hibernate/mapping/BasicValue.java | 2 +- .../EmbeddableRepresentationStrategyPojo.java | 4 +-- .../internal/InstantiationHelper.java | 2 +- .../sql/results/spi/ListResultsConsumer.java | 7 +++-- .../org/hibernate/type/BasicTypeRegistry.java | 4 +-- .../converter/internal/ConverterHelper.java | 30 +++++++++++-------- .../java/spi/CollectionJavaType.java | 6 ++-- .../hibernate/type/spi/TypeConfiguration.java | 4 +-- .../function/array/ArrayAggregateTest.java | 2 +- .../custom/CustomTypeConverterTest.java | 2 +- .../integration/customtype/EnumType.java | 4 ++- .../converter/GeometryConverterTest.java | 2 +- .../internal/CockroachTypeContributor.java | 4 +-- .../internal/DB2VectorTypeContributor.java | 6 ++-- .../internal/HANAVectorTypeContributor.java | 6 ++-- .../internal/MariaDBTypeContributor.java | 4 +-- .../vector/internal/MySQLTypeContributor.java | 4 +-- .../internal/OracleVectorTypeContributor.java | 10 +++---- .../internal/PGVectorTypeContributor.java | 8 ++--- .../internal/SQLServerTypeContributor.java | 4 +-- .../hibernate/vector/VectorTestHelper.java | 6 ++-- 34 files changed, 82 insertions(+), 73 deletions(-) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java index 83b691ec7557..0ca6c10e844d 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java @@ -405,7 +405,7 @@ protected void contributeCockroachTypes(TypeContributions typeContributions, Ser ObjectNullAsBinaryTypeJdbcType.INSTANCE, typeContributions.getTypeConfiguration() .getJavaTypeRegistry() - .getDescriptor( Object.class ) + .resolveDescriptor( Object.class ) ) ); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java index 82810235d43e..04270bd3699b 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java @@ -1135,7 +1135,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry ObjectNullResolvingJdbcType.INSTANCE, typeContributions.getTypeConfiguration() .getJavaTypeRegistry() - .getDescriptor( Object.class ) + .resolveDescriptor( Object.class ) ) ); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyDialect.java index 5cb7bc92015d..ab10429cd830 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyDialect.java @@ -671,7 +671,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry ObjectNullResolvingJdbcType.INSTANCE, typeContributions.getTypeConfiguration() .getJavaTypeRegistry() - .getDescriptor( Object.class ) + .resolveDescriptor( Object.class ) ) ); } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacyDialect.java index 38c77c1a2b65..2d31d2f6b2bf 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacyDialect.java @@ -666,7 +666,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry ObjectNullResolvingJdbcType.INSTANCE, typeContributions.getTypeConfiguration() .getJavaTypeRegistry() - .getDescriptor( Object.class ) + .resolveDescriptor( Object.class ) ) ); } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/GaussDBDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/GaussDBDialect.java index ec1882995618..c4abd86b5dc1 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/GaussDBDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/GaussDBDialect.java @@ -1270,7 +1270,7 @@ protected void contributeGaussDBTypes(TypeContributions typeContributions) { ObjectNullAsBinaryTypeJdbcType.INSTANCE, typeContributions.getTypeConfiguration() .getJavaTypeRegistry() - .getDescriptor( Object.class ) + .resolveDescriptor( Object.class ) ) ); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java index 18b4ba25d4a7..00743146139d 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java @@ -1128,7 +1128,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry ObjectNullAsBinaryTypeJdbcType.INSTANCE, typeContributions.getTypeConfiguration() .getJavaTypeRegistry() - .getDescriptor( Object.class ) + .resolveDescriptor( Object.class ) ) ); } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java index 0e9fadbb6f8c..66e67c1ac372 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java @@ -756,7 +756,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry NullJdbcType.INSTANCE, typeContributions.getTypeConfiguration() .getJavaTypeRegistry() - .getDescriptor( Object.class ) + .resolveDescriptor( Object.class ) ) ); } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java index c7a0a2d09167..fa53ea2c9357 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java @@ -1029,7 +1029,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry NullJdbcType.INSTANCE, typeContributions.getTypeConfiguration() .getJavaTypeRegistry() - .getDescriptor( Object.class ) + .resolveDescriptor( Object.class ) ) ); typeContributions.contributeType( @@ -1037,7 +1037,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry ObjectNullAsNullTypeJdbcType.INSTANCE, typeContributions.getTypeConfiguration() .getJavaTypeRegistry() - .getDescriptor( Object.class ) + .resolveDescriptor( Object.class ) ) ); } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java index 9d29ba01e9c3..53d56f931817 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java @@ -1558,7 +1558,7 @@ protected void contributePostgreSQLTypes(TypeContributions typeContributions, Se ObjectNullAsBinaryTypeJdbcType.INSTANCE, typeContributions.getTypeConfiguration() .getJavaTypeRegistry() - .getDescriptor( Object.class ) + .resolveDescriptor( Object.class ) ) ); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java index 76d2f97ecce8..f33e9d4cefa4 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java @@ -665,7 +665,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry NullJdbcType.INSTANCE, typeContributions.getTypeConfiguration() .getJavaTypeRegistry() - .getDescriptor( Object.class ) + .resolveDescriptor( Object.class ) ) ); jdbcTypeRegistry.addDescriptor( EnumJdbcType.INSTANCE ); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseLegacyDialect.java index d025598c4c15..1bdfdce0ff27 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseLegacyDialect.java @@ -262,7 +262,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry ObjectNullAsBinaryTypeJdbcType.INSTANCE, typeContributions.getTypeConfiguration() .getJavaTypeRegistry() - .getDescriptor( Object.class ) + .resolveDescriptor( Object.class ) ) ); typeContributions.contributeType( @@ -270,7 +270,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry ObjectNullAsBinaryTypeJdbcType.INSTANCE, typeContributions.getTypeConfiguration() .getJavaTypeRegistry() - .getDescriptor( Object.class ) + .resolveDescriptor( Object.class ) ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java index 6658fc70de8e..115841274d88 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java @@ -831,7 +831,7 @@ private void prepareAnyDiscriminator(MemberDetails memberDetails) { return originalResolution != null ? originalResolution : typeConfiguration.getJavaTypeRegistry() - .getDescriptor( implicitJavaTypeAccess.apply( typeConfiguration ) ) + .resolveDescriptor( implicitJavaTypeAccess.apply( typeConfiguration ) ) .getRecommendedJdbcType( typeConfiguration.getCurrentBaseSqlTypeIndicators() ); }; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/DdlTypeHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/DdlTypeHelper.java index c8e9be79381c..d8690ac17055 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/DdlTypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/DdlTypeHelper.java @@ -37,12 +37,12 @@ public static BasicType resolveArrayType(DomainType elementType, TypeConfi @SuppressWarnings("unchecked") public static BasicType resolveListType(DomainType elementType, TypeConfiguration typeConfiguration) { - final BasicPluralJavaType arrayJavaType = + final var arrayJavaType = (BasicPluralJavaType) typeConfiguration.getJavaTypeRegistry() - .getDescriptor( List.class ) + .resolveDescriptor( List.class ) .createJavaType( - new ParameterizedTypeImpl( List.class, new Type[]{ elementType.getJavaType() }, null ), + new ParameterizedTypeImpl( List.class, new Type[] { elementType.getJavaType() }, null ), typeConfiguration ); return arrayJavaType.resolveType( diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java b/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java index ff4db8c1906e..b132621570c6 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java @@ -779,7 +779,7 @@ private JavaType specialJavaType( return xmlJavaType; } } - return javaTypeRegistry.getDescriptor( impliedJavaType ); + return javaTypeRegistry.resolveDescriptor( impliedJavaType ); } private MutabilityPlan mutabilityPlan( diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EmbeddableRepresentationStrategyPojo.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EmbeddableRepresentationStrategyPojo.java index ff64b3d31b40..9c345ec97b82 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EmbeddableRepresentationStrategyPojo.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EmbeddableRepresentationStrategyPojo.java @@ -120,13 +120,13 @@ public EmbeddableRepresentationStrategyPojo( } } - private static JavaType resolveEmbeddableJavaType( + private static JavaType resolveEmbeddableJavaType( Component bootDescriptor, CompositeUserType compositeUserType, RuntimeModelCreationContext creationContext) { final var javaTypeRegistry = creationContext.getTypeConfiguration().getJavaTypeRegistry(); return compositeUserType == null - ? javaTypeRegistry.getDescriptor( bootDescriptor.getComponentClass() ) + ? javaTypeRegistry.resolveDescriptor( bootDescriptor.getComponentClass() ) : javaTypeRegistry.resolveDescriptor( compositeUserType.returnedClass(), () -> new CompositeUserTypeJavaTypeWrapper<>( compositeUserType ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/InstantiationHelper.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/InstantiationHelper.java index d41a125b6473..48c33a6ef997 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/InstantiationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/InstantiationHelper.java @@ -83,7 +83,7 @@ public static boolean isConstructorCompatible( final var type = parameterType instanceof Class classParameter ? classParameter - : typeConfiguration.getJavaTypeRegistry().getDescriptor( parameterType ) + : typeConfiguration.getJavaTypeRegistry().resolveDescriptor( parameterType ) .getJavaTypeClass(); if ( !areAssignmentCompatible( type, argumentType ) ) { if ( LOG.isDebugEnabled() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java index 1698411f91c7..619cc4e01e19 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java @@ -290,10 +290,11 @@ private JavaType resolveDomainResultJavaType( final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry(); if ( domainResultResultJavaType != null ) { - final JavaType resultJavaType = javaTypeRegistry.resolveDescriptor( domainResultResultJavaType ); + final var resultJavaType = javaTypeRegistry.resolveDescriptor( domainResultResultJavaType ); // Could be that the user requested a more general type than the actual type, // so resolve the most concrete type since this type is used to determine equality of objects - if ( resultJavaTypes.size() == 1 && isMoreConcrete( resultJavaType, resultJavaTypes.get( 0 ) ) ) { + if ( resultJavaTypes.size() == 1 + && isMoreConcrete( resultJavaType, resultJavaTypes.get( 0 ) ) ) { //noinspection unchecked return (JavaType) resultJavaTypes.get( 0 ); } @@ -301,7 +302,7 @@ private JavaType resolveDomainResultJavaType( } if ( resultJavaTypes.size() == 1 ) { - final JavaType firstJavaType = resultJavaTypes.get( 0 ); + final var firstJavaType = resultJavaTypes.get( 0 ); if ( firstJavaType == null ) { return javaTypeRegistry.getDescriptor( Object.class ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java b/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java index 1127a18db077..4b437c0184f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java @@ -137,9 +137,9 @@ public BasicType resolve(Class javaType, int sqlTypeCode) { return resolve( getJavaTypeRegistry().resolveDescriptor( javaType ), sqlTypeCode ); } - @Deprecated(since = "7.2", forRemoval = true) // no longer used + calls deprecated method + // no longer used public BasicType resolve(java.lang.reflect.Type javaType, int sqlTypeCode) { - return resolve( getJavaTypeRegistry().getDescriptor( javaType ), sqlTypeCode ); + return resolve( getJavaTypeRegistry().resolveDescriptor( javaType ), sqlTypeCode ); } public BasicType resolve(JavaType javaType, int sqlTypeCode) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/ConverterHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/ConverterHelper.java index 5c2b3ca5d8b0..ae5117c1e6a0 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/ConverterHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/ConverterHelper.java @@ -21,28 +21,34 @@ public class ConverterHelper { public static BasicValueConverter createValueConverter( AttributeConverter converter, JavaTypeRegistry registry) { - final var converterType = extractParameterizedType( converter.getClass(), AttributeConverter.class ); - final var typeArguments = converterType.getActualTypeArguments(); - final var domainJavaClass = extractClass( typeArguments[0] ); - final var relationalJavaClass = extractClass( typeArguments[1] ); + final var typeArguments = + extractParameterizedType( converter.getClass(), AttributeConverter.class ) + .getActualTypeArguments(); + @SuppressWarnings("unchecked") // perfectly safe + final var domainJavaClass = (Class) extractClass( typeArguments[0] ); + @SuppressWarnings("unchecked") // perfectly safe + final var relationalJavaClass = (Class) extractClass( typeArguments[1] ); return new AttributeConverterInstance<>( converter, - registry.getDescriptor( domainJavaClass ), - registry.getDescriptor( relationalJavaClass ) + registry.resolveDescriptor( domainJavaClass ), + registry.resolveDescriptor( relationalJavaClass ) ); } public static JpaAttributeConverter createJpaAttributeConverter( ManagedBean> bean, JavaTypeRegistry registry) { - final var converterType = extractParameterizedType( bean.getBeanClass(), AttributeConverter.class ); - final var typeArguments = converterType.getActualTypeArguments(); - final var domainJavaClass = extractClass( typeArguments[0] ); - final var relationalJavaClass = extractClass( typeArguments[1] ); + final var typeArguments = + extractParameterizedType( bean.getBeanClass(), AttributeConverter.class ) + .getActualTypeArguments(); + @SuppressWarnings("unchecked") // perfectly safe + final var domainJavaClass = (Class) extractClass( typeArguments[0] ); + @SuppressWarnings("unchecked") // perfectly safe + final var relationalJavaClass = (Class) extractClass( typeArguments[1] ); return new AttributeConverterBean<>( bean, registry.resolveDescriptor( bean.getBeanClass() ), - registry.getDescriptor( domainJavaClass ), - registry.getDescriptor( relationalJavaClass ) + registry.resolveDescriptor( domainJavaClass ), + registry.resolveDescriptor( relationalJavaClass ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/CollectionJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/CollectionJavaType.java index 06ae24e597ba..417f4f051977 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/CollectionJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/CollectionJavaType.java @@ -65,7 +65,7 @@ public JavaType createJavaType( case BAG, ID_BAG, LIST, SET, SORTED_SET, ORDERED_SET -> new BasicCollectionJavaType( parameterizedType, - registry.getDescriptor( typeArguments[typeArguments.length-1] ), + registry.resolveDescriptor( typeArguments[typeArguments.length-1] ), semantics ); case MAP, ORDERED_MAP, SORTED_MAP -> @@ -74,8 +74,8 @@ public JavaType createJavaType( parameterizedType, new MapMutabilityPlan( (MapSemantics) semantics, - registry.getDescriptor( typeArguments[0] ), - registry.getDescriptor( typeArguments[typeArguments.length-1] ) + registry.resolveDescriptor( typeArguments[0] ), + registry.resolveDescriptor( typeArguments[typeArguments.length-1] ) ) ); }; diff --git a/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java b/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java index daea60d36aed..f64346c4cf0a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java +++ b/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java @@ -847,7 +847,7 @@ public BasicType standardBasicTypeForJavaType( final var registeredType = basicTypeRegistry.getRegisteredType( javaClass ); return registeredType == null - ? creator.apply( javaTypeRegistry.getDescriptor( javaClass ) ) + ? creator.apply( javaTypeRegistry.resolveDescriptor( javaClass ) ) : registeredType; } } @@ -870,7 +870,7 @@ public BasicType standardBasicTypeForJavaType( //noinspection unchecked return registeredType == null ? creator.apply( (JavaType) // UNCHECKED CAST - javaTypeRegistry.getDescriptor( javaType ) ) + javaTypeRegistry.resolveDescriptor( javaType ) ) : registeredType; } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayAggregateTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayAggregateTest.java index f1faea74a561..95bbd9981859 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayAggregateTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/function/array/ArrayAggregateTest.java @@ -80,7 +80,7 @@ public void contribute( jdbcTypeRegistry.getDescriptor( SqlTypes.VARCHAR ), "StringArray" ).addAuxiliaryDatabaseObjects( - new ArrayJavaType<>( javaTypeRegistry.getDescriptor( String.class ) ), + new ArrayJavaType<>( javaTypeRegistry.resolveDescriptor( String.class ) ), null, Size.nil(), metadata.getDatabase(), diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/custom/CustomTypeConverterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/custom/CustomTypeConverterTest.java index 20a01a05e3a5..91d8d6e57eb7 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/custom/CustomTypeConverterTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/custom/CustomTypeConverterTest.java @@ -51,7 +51,7 @@ protected void performAssertions( ); assertThat( - bootTypeConfiguration.getJavaTypeRegistry().getDescriptor( PayloadWrapper.class ), + bootTypeConfiguration.getJavaTypeRegistry().resolveDescriptor( PayloadWrapper.class ), sameInstance( PayloadWrapperJavaType.INSTANCE ) ); diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/customtype/EnumType.java b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/customtype/EnumType.java index e077434751c8..0c2e69257484 100644 --- a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/customtype/EnumType.java +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/customtype/EnumType.java @@ -116,7 +116,9 @@ else if ( reader != null ) { enumClass = (Class) reader.getReturnedClass().asSubclass( Enum.class ); } - final JavaType descriptor = typeConfiguration.getJavaTypeRegistry().getDescriptor( enumClass ); + final JavaType descriptor = + typeConfiguration.getJavaTypeRegistry() + .resolveDescriptor( enumClass ); enumJavaType = (EnumJavaType) descriptor; if ( parameters.containsKey( TYPE ) ) { diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/converter/GeometryConverterTest.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/converter/GeometryConverterTest.java index 452fc84007ed..a3d09bced78b 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/converter/GeometryConverterTest.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/converter/GeometryConverterTest.java @@ -48,7 +48,7 @@ public void testConverterUsage() { final TypeConfiguration typeConfiguration = sessionFactory.getMappingMetamodel().getTypeConfiguration(); - assertThat( typeConfiguration.getJavaTypeRegistry().getDescriptor( Geometry.class ) ) + assertThat( typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Geometry.class ) ) .isSameAs( GeolatteGeometryJavaType.GEOMETRY_INSTANCE ); // todo (5.3) : what to assert wrt to SqlTypeDescriptor? Anything? diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/CockroachTypeContributor.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/CockroachTypeContributor.java index 4680b984c17d..f80c1b90d3dc 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/CockroachTypeContributor.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/CockroachTypeContributor.java @@ -47,7 +47,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( floatBasicType, genericVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR.getName() ); @@ -55,7 +55,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.FLOAT ), floatVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR_FLOAT32.getName() ); diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/DB2VectorTypeContributor.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/DB2VectorTypeContributor.java index 83a2a62d1b3c..ab843cc31936 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/DB2VectorTypeContributor.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/DB2VectorTypeContributor.java @@ -48,7 +48,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( floatBasicType, genericVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR.getName() ); @@ -56,7 +56,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.FLOAT ), floatVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR_FLOAT32.getName() ); @@ -64,7 +64,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.BYTE ), byteVectorJdbcType, - javaTypeRegistry.getDescriptor( byte[].class ) + javaTypeRegistry.resolveDescriptor( byte[].class ) ), StandardBasicTypes.VECTOR_INT8.getName() ); diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/HANAVectorTypeContributor.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/HANAVectorTypeContributor.java index 599e416312bc..8723047ac9af 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/HANAVectorTypeContributor.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/HANAVectorTypeContributor.java @@ -46,7 +46,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( floatBasicType, genericVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR.getName() ); @@ -54,7 +54,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.FLOAT ), floatVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR_FLOAT32.getName() ); @@ -77,7 +77,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.FLOAT ), float16VectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR_FLOAT16.getName() ); diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/MariaDBTypeContributor.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/MariaDBTypeContributor.java index b56662a1185d..22c16d8e6576 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/MariaDBTypeContributor.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/MariaDBTypeContributor.java @@ -45,7 +45,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( floatBasicType, genericVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR.getName() ); @@ -53,7 +53,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.FLOAT ), floatVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR_FLOAT32.getName() ); diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/MySQLTypeContributor.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/MySQLTypeContributor.java index d6bbfeba9528..18c93371242b 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/MySQLTypeContributor.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/MySQLTypeContributor.java @@ -45,7 +45,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( floatBasicType, genericVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR.getName() ); @@ -53,7 +53,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.FLOAT ), floatVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR_FLOAT32.getName() ); diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/OracleVectorTypeContributor.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/OracleVectorTypeContributor.java index 4e6347b97dfe..c8b337e8f4ab 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/OracleVectorTypeContributor.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/OracleVectorTypeContributor.java @@ -85,7 +85,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.FLOAT ), genericVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR.getName() ); @@ -93,7 +93,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.FLOAT ), floatVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR_FLOAT32.getName() ); @@ -101,7 +101,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.DOUBLE ), doubleVectorJdbcType, - javaTypeRegistry.getDescriptor( double[].class ) + javaTypeRegistry.resolveDescriptor( double[].class ) ), StandardBasicTypes.VECTOR_FLOAT64.getName() ); @@ -109,7 +109,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.BYTE ), byteVectorJdbcType, - javaTypeRegistry.getDescriptor( byte[].class ) + javaTypeRegistry.resolveDescriptor( byte[].class ) ), StandardBasicTypes.VECTOR_INT8.getName() ); @@ -117,7 +117,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.BYTE ), bitVectorJdbcType, - javaTypeRegistry.getDescriptor( byte[].class ) + javaTypeRegistry.resolveDescriptor( byte[].class ) ), StandardBasicTypes.VECTOR_BINARY.getName() ); diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGVectorTypeContributor.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGVectorTypeContributor.java index 352bc4bb28ad..cbca1c9eaf42 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGVectorTypeContributor.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGVectorTypeContributor.java @@ -66,7 +66,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( floatBasicType, genericVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR.getName() ); @@ -74,7 +74,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.FLOAT ), floatVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR_FLOAT32.getName() ); @@ -82,7 +82,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.FLOAT ), float16VectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR_FLOAT16.getName() ); @@ -90,7 +90,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.BYTE ), bitVectorJdbcType, - javaTypeRegistry.getDescriptor( byte[].class ) + javaTypeRegistry.resolveDescriptor( byte[].class ) ), StandardBasicTypes.VECTOR_BINARY.getName() ); diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/SQLServerTypeContributor.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/SQLServerTypeContributor.java index 9c095e51871c..86ef700c87ac 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/SQLServerTypeContributor.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/SQLServerTypeContributor.java @@ -59,7 +59,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( floatBasicType, genericVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR.getName() ); @@ -67,7 +67,7 @@ public void contribute(TypeContributions typeContributions, ServiceRegistry serv new BasicArrayType<>( basicTypeRegistry.resolve( StandardBasicTypes.FLOAT ), floatVectorJdbcType, - javaTypeRegistry.getDescriptor( float[].class ) + javaTypeRegistry.resolveDescriptor( float[].class ) ), StandardBasicTypes.VECTOR_FLOAT32.getName() ); diff --git a/hibernate-vector/src/test/java/org/hibernate/vector/VectorTestHelper.java b/hibernate-vector/src/test/java/org/hibernate/vector/VectorTestHelper.java index 3edc584d1315..091bb51de414 100644 --- a/hibernate-vector/src/test/java/org/hibernate/vector/VectorTestHelper.java +++ b/hibernate-vector/src/test/java/org/hibernate/vector/VectorTestHelper.java @@ -268,7 +268,7 @@ public static String vectorSparseStringLiteral(float[] vector, SessionImplemento final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration(); final JdbcLiteralFormatter literalFormatter = typeConfiguration.getJdbcTypeRegistry() .getDescriptor( SqlTypes.SPARSE_VECTOR_FLOAT32 ) - .getJdbcLiteralFormatter( typeConfiguration.getJavaTypeRegistry().getDescriptor( SparseFloatVector.class ) ); + .getJdbcLiteralFormatter( typeConfiguration.getJavaTypeRegistry().resolveDescriptor( SparseFloatVector.class ) ); final String jdbcLiteral = literalFormatter.toJdbcLiteral( new SparseFloatVector( vector ), sessionFactory.getJdbcServices().getDialect(), @@ -284,7 +284,7 @@ public static String vectorSparseStringLiteral(double[] vector, SessionImplement final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration(); final JdbcLiteralFormatter literalFormatter = typeConfiguration.getJdbcTypeRegistry() .getDescriptor( SqlTypes.SPARSE_VECTOR_FLOAT64 ) - .getJdbcLiteralFormatter( typeConfiguration.getJavaTypeRegistry().getDescriptor( SparseDoubleVector.class ) ); + .getJdbcLiteralFormatter( typeConfiguration.getJavaTypeRegistry().resolveDescriptor( SparseDoubleVector.class ) ); final String jdbcLiteral = literalFormatter.toJdbcLiteral( new SparseDoubleVector( vector ), sessionFactory.getJdbcServices().getDialect(), @@ -300,7 +300,7 @@ public static String vectorSparseStringLiteral(byte[] vector, SessionImplementor final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration(); final JdbcLiteralFormatter literalFormatter = typeConfiguration.getJdbcTypeRegistry() .getDescriptor( SqlTypes.SPARSE_VECTOR_INT8 ) - .getJdbcLiteralFormatter( typeConfiguration.getJavaTypeRegistry().getDescriptor( SparseByteVector.class ) ); + .getJdbcLiteralFormatter( typeConfiguration.getJavaTypeRegistry().resolveDescriptor( SparseByteVector.class ) ); final String jdbcLiteral = literalFormatter.toJdbcLiteral( new SparseByteVector( vector ), sessionFactory.getJdbcServices().getDialect(), From 8a2f5d4f48db987e7215caac3e80a0b25da2ba62 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 8 Dec 2025 13:33:37 +0100 Subject: [PATCH 056/181] fix the lack of type safety of getJdbcRecommendedJavaTypeMapping() this depended on the use of uncheckable casts to generic types enabled much less use of JavaTypeRegistry.getDescriptor() --- .../internal/InferredBasicValueResolver.java | 8 +- .../xml/internal/DynamicModelHelper.java | 141 +++++++++--------- .../AbstractPostgreSQLStructJdbcType.java | 12 +- .../dialect/type/DB2StructJdbcType.java | 12 +- .../type/OracleArrayJdbcTypeConstructor.java | 2 +- .../OracleNestedTableJdbcTypeConstructor.java | 2 +- .../org/hibernate/mapping/BasicValue.java | 8 +- .../internal/FunctionReturnImpl.java | 2 +- .../internal/AbstractResultSetAccess.java | 7 +- .../sql/results/spi/ListResultsConsumer.java | 24 ++- .../java/org/hibernate/type/CustomType.java | 2 +- .../type/descriptor/java/JavaType.java | 2 +- .../type/descriptor/jdbc/ArrayJdbcType.java | 18 +-- .../type/descriptor/jdbc/BigIntJdbcType.java | 4 +- .../type/descriptor/jdbc/BlobJdbcType.java | 4 +- .../type/descriptor/jdbc/BooleanJdbcType.java | 4 +- .../type/descriptor/jdbc/DateJdbcType.java | 6 +- .../type/descriptor/jdbc/DecimalJdbcType.java | 4 +- .../type/descriptor/jdbc/DoubleJdbcType.java | 4 +- .../type/descriptor/jdbc/FloatJdbcType.java | 6 +- .../type/descriptor/jdbc/IntegerJdbcType.java | 4 +- .../type/descriptor/jdbc/JdbcType.java | 37 +++-- .../descriptor/jdbc/NVarcharJdbcType.java | 6 +- .../type/descriptor/jdbc/RealJdbcType.java | 4 +- .../descriptor/jdbc/SmallIntJdbcType.java | 4 +- .../type/descriptor/jdbc/StructJdbcType.java | 12 +- .../TimeAsTimestampWithTimeZoneJdbcType.java | 4 +- .../type/descriptor/jdbc/TimeJdbcType.java | 6 +- .../jdbc/TimeUtcAsJdbcTimeJdbcType.java | 4 +- .../jdbc/TimeUtcAsOffsetTimeJdbcType.java | 4 +- .../jdbc/TimeWithTimeZoneJdbcType.java | 4 +- .../descriptor/jdbc/TimestampJdbcType.java | 6 +- .../jdbc/TimestampUtcAsInstantJdbcType.java | 4 +- .../TimestampUtcAsJdbcTimestampJdbcType.java | 4 +- .../TimestampUtcAsOffsetDateTimeJdbcType.java | 4 +- .../jdbc/TimestampWithTimeZoneJdbcType.java | 4 +- .../type/descriptor/jdbc/TinyIntJdbcType.java | 4 +- .../descriptor/jdbc/VarbinaryJdbcType.java | 4 +- .../type/descriptor/jdbc/VarcharJdbcType.java | 4 +- .../internal/AbstractJavaTimeJdbcType.java | 4 +- .../jdbc/internal/DelayedStructJdbcType.java | 5 +- .../internal/UserTypeJdbcTypeAdapter.java | 5 +- .../test/id/usertype/inet/InetJdbcType.java | 4 +- .../array/StringArrayJdbcType.java | 4 +- .../vector/internal/HANAVectorJdbcType.java | 4 +- .../internal/MariaDBVectorJdbcType.java | 4 +- .../vector/internal/MySQLVectorJdbcType.java | 4 +- .../internal/PGBinaryVectorJdbcType.java | 4 +- .../internal/PGSparseFloatVectorJdbcType.java | 4 +- .../vector/internal/PGVectorJdbcType.java | 4 +- .../SQLServerCastingVectorJdbcType.java | 4 +- 51 files changed, 220 insertions(+), 225 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java index 11bc77e5d211..e00feb694a43 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java @@ -194,7 +194,7 @@ else if ( reflectedJtd instanceof SerializableJavaType else { if ( explicitJdbcType != null ) { // we have an explicit STD, but no JTD - infer JTD - // NOTE : yes it's an odd case, but easy to implement here, so... + // NOTE: yes it's an odd case, but easy to implement here, so... Integer length = null; Integer scale = null; if ( selectable instanceof Column column ) { @@ -212,8 +212,10 @@ else if ( column.getLength() != null ) { } } - final JavaType recommendedJtd = - explicitJdbcType.getJdbcRecommendedJavaTypeMapping( length, scale, typeConfiguration ); + final var recommendedJavaType = + explicitJdbcType.getRecommendedJavaType( length, scale, typeConfiguration ); + // TODO: check this type cast + final var recommendedJtd = (JavaType) recommendedJavaType; jdbcMapping = resolveSqlTypeIndicators( stdIndicators, basicTypeRegistry.resolve( recommendedJtd, explicitJdbcType ), diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/DynamicModelHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/DynamicModelHelper.java index 9894f3953083..fb689fb3df4b 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/DynamicModelHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/DynamicModelHelper.java @@ -11,26 +11,20 @@ import org.hibernate.boot.jaxb.mapping.spi.JaxbAnyMapping; import org.hibernate.boot.jaxb.mapping.spi.JaxbAssociationAttribute; import org.hibernate.boot.jaxb.mapping.spi.JaxbAttributesContainer; -import org.hibernate.boot.jaxb.mapping.spi.JaxbAttributesContainerImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbBasicImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbBasicMapping; import org.hibernate.boot.jaxb.mapping.spi.JaxbElementCollectionImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbEmbeddable; -import org.hibernate.boot.jaxb.mapping.spi.JaxbEmbeddedIdImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbEmbeddedMapping; import org.hibernate.boot.jaxb.mapping.spi.JaxbEntity; import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbManagedType; import org.hibernate.boot.jaxb.mapping.spi.JaxbMappedSuperclassImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbPluralAttribute; -import org.hibernate.boot.jaxb.mapping.spi.JaxbTenantIdImpl; -import org.hibernate.boot.jaxb.mapping.spi.JaxbUserTypeImpl; import org.hibernate.boot.models.internal.ModelsHelper; import org.hibernate.boot.models.xml.UnknownAttributeTypeException; import org.hibernate.boot.models.xml.spi.XmlDocumentContext; import org.hibernate.boot.spi.BootstrapContext; -import org.hibernate.internal.util.StringHelper; -import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.models.internal.ClassTypeDetailsImpl; import org.hibernate.models.internal.MutableClassDetailsRegistry; import org.hibernate.models.internal.ParameterizedTypeDetailsImpl; @@ -39,7 +33,6 @@ import org.hibernate.models.internal.dynamic.DynamicFieldDetails; import org.hibernate.models.internal.jdk.JdkClassDetails; import org.hibernate.models.spi.ClassDetails; -import org.hibernate.models.spi.ClassDetailsRegistry; import org.hibernate.models.spi.ModelsContext; import org.hibernate.models.spi.MutableClassDetails; import org.hibernate.models.spi.TypeDetails; @@ -58,6 +51,9 @@ import static org.hibernate.internal.util.NullnessHelper.nullif; import static org.hibernate.internal.util.ReflectHelper.OBJECT_CLASS_NAME; import static org.hibernate.internal.util.StringHelper.isNotEmpty; +import static org.hibernate.internal.util.StringHelper.isNotEmpty; +import static org.hibernate.internal.util.StringHelper.nullIfEmpty; +import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty; import static org.hibernate.models.internal.ModifierUtils.DYNAMIC_ATTRIBUTE_MODIFIERS; /** @@ -74,10 +70,9 @@ static void prepareDynamicClass( JaxbManagedType jaxbManagedType, XmlDocumentContext xmlDocumentContext) { if ( jaxbManagedType instanceof JaxbEntityImpl jaxbDynamicEntity ) { - final JaxbAttributesContainerImpl attributes = jaxbDynamicEntity.getAttributes(); - + final var attributes = jaxbDynamicEntity.getAttributes(); if ( attributes != null ) { - if ( CollectionHelper.isNotEmpty( attributes.getIdAttributes() ) ) { + if ( isNotEmpty( attributes.getIdAttributes() ) ) { // attributes.getIdAttributes().forEach( (jaxbId) -> { final TypeDetails attributeJavaType = determineAttributeJavaTypeDetails( @@ -99,13 +94,13 @@ static void prepareDynamicClass( } else if ( attributes.getEmbeddedIdAttribute() != null ) { // - final JaxbEmbeddedIdImpl embeddedId = attributes.getEmbeddedIdAttribute(); - final TypeDetails attributeJavaType = determineAttributeJavaTypeDetails( + final var embeddedId = attributes.getEmbeddedIdAttribute(); + final var attributeJavaType = determineAttributeJavaTypeDetails( jaxbManagedType, embeddedId, xmlDocumentContext ); - final DynamicFieldDetails member = new DynamicFieldDetails( + final var member = new DynamicFieldDetails( embeddedId.getName(), attributeJavaType, classDetails, @@ -118,14 +113,15 @@ else if ( attributes.getEmbeddedIdAttribute() != null ) { } // - if ( attributes.getNaturalId() != null ) { - attributes.getNaturalId().getBasicAttributes().forEach( (jaxbBasic) -> { - final TypeDetails attributeJavaType = determineAttributeJavaTypeDetails( + final var naturalId = attributes.getNaturalId(); + if ( naturalId != null ) { + naturalId.getBasicAttributes().forEach( (jaxbBasic) -> { + final var attributeJavaType = determineAttributeJavaTypeDetails( jaxbManagedType, jaxbBasic, xmlDocumentContext ); - final DynamicFieldDetails member = new DynamicFieldDetails( + final var member = new DynamicFieldDetails( jaxbBasic.getName(), attributeJavaType, classDetails, @@ -137,13 +133,13 @@ else if ( attributes.getEmbeddedIdAttribute() != null ) { classDetails.addField( member ); } ); - attributes.getNaturalId().getEmbeddedAttributes().forEach( (jaxbEmbedded) -> { - final TypeDetails attributeJavaType = determineAttributeJavaTypeDetails( + naturalId.getEmbeddedAttributes().forEach( (jaxbEmbedded) -> { + final var attributeJavaType = determineAttributeJavaTypeDetails( jaxbManagedType, jaxbEmbedded, xmlDocumentContext ); - final DynamicFieldDetails member = new DynamicFieldDetails( + final var member = new DynamicFieldDetails( jaxbEmbedded.getName(), attributeJavaType, classDetails, @@ -155,12 +151,12 @@ else if ( attributes.getEmbeddedIdAttribute() != null ) { classDetails.addField( member ); } ); - attributes.getNaturalId().getManyToOneAttributes().forEach( (jaxbManyToOne) -> { - final TypeDetails attributeJavaType = determineAttributeJavaTypeDetails( + naturalId.getManyToOneAttributes().forEach( (jaxbManyToOne) -> { + final var attributeJavaType = determineAttributeJavaTypeDetails( jaxbManyToOne, xmlDocumentContext ); - final DynamicFieldDetails member = new DynamicFieldDetails( + final var member = new DynamicFieldDetails( jaxbManyToOne.getName(), attributeJavaType, classDetails, @@ -172,12 +168,12 @@ else if ( attributes.getEmbeddedIdAttribute() != null ) { classDetails.addField( member ); } ); - attributes.getNaturalId().getAnyMappingAttributes().forEach( (jaxbAnyMapping) -> { - final TypeDetails attributeJavaType = determineAttributeJavaTypeDetails( + naturalId.getAnyMappingAttributes().forEach( (jaxbAnyMapping) -> { + final var attributeJavaType = determineAttributeJavaTypeDetails( jaxbAnyMapping, xmlDocumentContext ); - final DynamicFieldDetails member = new DynamicFieldDetails( + final var member = new DynamicFieldDetails( jaxbAnyMapping.getName(), attributeJavaType, classDetails, @@ -192,14 +188,14 @@ else if ( attributes.getEmbeddedIdAttribute() != null ) { } // - final JaxbTenantIdImpl tenantId = jaxbDynamicEntity.getTenantId(); + final var tenantId = jaxbDynamicEntity.getTenantId(); if ( tenantId != null ) { - final TypeDetails attributeJavaType = determineAttributeJavaTypeDetails( + final var attributeJavaType = determineAttributeJavaTypeDetails( jaxbManagedType, tenantId, xmlDocumentContext ); - final DynamicFieldDetails member = new DynamicFieldDetails( + final var member = new DynamicFieldDetails( tenantId.getName(), attributeJavaType, classDetails, @@ -212,18 +208,17 @@ else if ( attributes.getEmbeddedIdAttribute() != null ) { } } else if ( jaxbManagedType instanceof JaxbMappedSuperclassImpl jaxbMappedSuperclass ) { - final JaxbAttributesContainerImpl attributes = jaxbMappedSuperclass.getAttributes(); - + final var attributes = jaxbMappedSuperclass.getAttributes(); if ( attributes != null ) { - if ( CollectionHelper.isNotEmpty( attributes.getIdAttributes() ) ) { + if ( isNotEmpty( attributes.getIdAttributes() ) ) { // attributes.getIdAttributes().forEach( (jaxbId) -> { - final TypeDetails attributeJavaType = determineAttributeJavaTypeDetails( + final var attributeJavaType = determineAttributeJavaTypeDetails( jaxbManagedType, jaxbId, xmlDocumentContext ); - final DynamicFieldDetails member = new DynamicFieldDetails( + final var member = new DynamicFieldDetails( jaxbId.getName(), attributeJavaType, classDetails, @@ -237,13 +232,13 @@ else if ( jaxbManagedType instanceof JaxbMappedSuperclassImpl jaxbMappedSupercla } else { // - final JaxbEmbeddedIdImpl embeddedId = attributes.getEmbeddedIdAttribute(); - final TypeDetails attributeJavaType = determineAttributeJavaTypeDetails( + final var embeddedId = attributes.getEmbeddedIdAttribute(); + final var attributeJavaType = determineAttributeJavaTypeDetails( jaxbManagedType, embeddedId, xmlDocumentContext ); - final DynamicFieldDetails member = new DynamicFieldDetails( + final var member = new DynamicFieldDetails( embeddedId.getName(), attributeJavaType, classDetails, @@ -262,7 +257,7 @@ else if ( jaxbManagedType instanceof JaxbMappedSuperclassImpl jaxbMappedSupercla if ( attributes != null ) { // attributes.getBasicAttributes().forEach( (jaxbBasic) -> { - final DynamicFieldDetails member = new DynamicFieldDetails( + final var member = new DynamicFieldDetails( jaxbBasic.getName(), determineAttributeJavaTypeDetails( jaxbManagedType, jaxbBasic, xmlDocumentContext ), classDetails, @@ -276,7 +271,7 @@ else if ( jaxbManagedType instanceof JaxbMappedSuperclassImpl jaxbMappedSupercla // attributes.getEmbeddedAttributes().forEach( (jaxbEmbedded) -> { - final DynamicFieldDetails member = new DynamicFieldDetails( + final var member = new DynamicFieldDetails( jaxbEmbedded.getName(), determineAttributeJavaTypeDetails( jaxbManagedType, jaxbEmbedded, xmlDocumentContext ), classDetails, @@ -290,7 +285,7 @@ else if ( jaxbManagedType instanceof JaxbMappedSuperclassImpl jaxbMappedSupercla // attributes.getOneToOneAttributes().forEach( (jaxbOneToOne) -> { - final DynamicFieldDetails member = new DynamicFieldDetails( + final var member = new DynamicFieldDetails( jaxbOneToOne.getName(), determineAttributeJavaTypeDetails( jaxbOneToOne, xmlDocumentContext ), classDetails, @@ -304,7 +299,7 @@ else if ( jaxbManagedType instanceof JaxbMappedSuperclassImpl jaxbMappedSupercla // attributes.getManyToOneAttributes().forEach( (jaxbManyToOne) -> { - final DynamicFieldDetails member = new DynamicFieldDetails( + final var member = new DynamicFieldDetails( jaxbManyToOne.getName(), determineAttributeJavaTypeDetails( jaxbManyToOne, xmlDocumentContext ), classDetails, @@ -318,7 +313,7 @@ else if ( jaxbManagedType instanceof JaxbMappedSuperclassImpl jaxbMappedSupercla // attributes.getAnyMappingAttributes().forEach( (jaxbAnyMapping) -> { - final DynamicFieldDetails member = new DynamicFieldDetails( + final var member = new DynamicFieldDetails( jaxbAnyMapping.getName(), determineAttributeJavaTypeDetails( jaxbAnyMapping, xmlDocumentContext ), classDetails, @@ -332,8 +327,8 @@ else if ( jaxbManagedType instanceof JaxbMappedSuperclassImpl jaxbMappedSupercla // attributes.getElementCollectionAttributes().forEach( (jaxbElementCollection) -> { - final TypeDetails elementType = determineAttributeJavaTypeDetails( jaxbElementCollection, xmlDocumentContext ); - final DynamicFieldDetails member = new DynamicFieldDetails( + final var elementType = determineAttributeJavaTypeDetails( jaxbElementCollection, xmlDocumentContext ); + final var member = new DynamicFieldDetails( jaxbElementCollection.getName(), makeCollectionType( classDetails, jaxbElementCollection, elementType, xmlDocumentContext ), classDetails, @@ -347,8 +342,8 @@ else if ( jaxbManagedType instanceof JaxbMappedSuperclassImpl jaxbMappedSupercla // attributes.getOneToManyAttributes().forEach( (jaxbOneToMany) -> { - final TypeDetails elementType = determineAttributeJavaTypeDetails( jaxbOneToMany, xmlDocumentContext ); - final DynamicFieldDetails member = new DynamicFieldDetails( + final var elementType = determineAttributeJavaTypeDetails( jaxbOneToMany, xmlDocumentContext ); + final var member = new DynamicFieldDetails( jaxbOneToMany.getName(), // todo : this is wrong. should be the collection-type (List, ...) // wrapping the result from determineAttributeJavaTypeDetails @@ -364,8 +359,8 @@ else if ( jaxbManagedType instanceof JaxbMappedSuperclassImpl jaxbMappedSupercla // attributes.getManyToManyAttributes().forEach( (jaxbManyToMany) -> { - final TypeDetails elementType = determineAttributeJavaTypeDetails( jaxbManyToMany, xmlDocumentContext ); - final DynamicFieldDetails member = new DynamicFieldDetails( + final var elementType = determineAttributeJavaTypeDetails( jaxbManyToMany, xmlDocumentContext ); + final var member = new DynamicFieldDetails( jaxbManyToMany.getName(), makeCollectionType( classDetails, jaxbManyToMany, elementType, xmlDocumentContext ), classDetails, @@ -379,11 +374,11 @@ else if ( jaxbManagedType instanceof JaxbMappedSuperclassImpl jaxbMappedSupercla // attributes.getPluralAnyMappingAttributes().forEach( (jaxbPluralAnyMapping) -> { - final TypeDetails attributeType = determineAttributeJavaTypeDetails( + final var attributeType = determineAttributeJavaTypeDetails( jaxbPluralAnyMapping, xmlDocumentContext ); - final DynamicFieldDetails member = new DynamicFieldDetails( + final var member = new DynamicFieldDetails( jaxbPluralAnyMapping.getName(), attributeType, classDetails, @@ -409,7 +404,7 @@ private static TypeDetails makeCollectionType( JaxbPluralAttribute jaxbPluralAttribute, TypeDetails elementType, XmlDocumentContext xmlDocumentContext) { - final MutableClassDetailsRegistry classDetailsRegistry = xmlDocumentContext + final var classDetailsRegistry = xmlDocumentContext .getBootstrapContext() .getModelsContext() .getClassDetailsRegistry() @@ -484,9 +479,9 @@ private static ClassDetails setType(JaxbPluralAttribute jaxbPluralAttribute, Mut } private static boolean isSorted(JaxbPluralAttribute jaxbPluralAttribute) { - return StringHelper.isNotEmpty( jaxbPluralAttribute.getSort() ) + return isNotEmpty( jaxbPluralAttribute.getSort() ) || jaxbPluralAttribute.getSortNatural() != null - || StringHelper.isNotEmpty( jaxbPluralAttribute.getOrderBy() ); + || isNotEmpty( jaxbPluralAttribute.getOrderBy() ); } private static ClassDetails mapType(JaxbPluralAttribute jaxbPluralAttribute, MutableClassDetailsRegistry classDetailsRegistry) { @@ -523,7 +518,7 @@ private static ClassDetails determineAttributeJavaType( // explicit final String target = jaxbBasicMapping.getTarget(); if ( isNotEmpty( target ) ) { - final SimpleTypeInterpretation simpleTypeInterpretation = SimpleTypeInterpretation.interpret( target ); + final var simpleTypeInterpretation = SimpleTypeInterpretation.interpret( target ); if ( simpleTypeInterpretation == null ) { throw new UnknownAttributeTypeException( String.format( @@ -541,7 +536,7 @@ private static ClassDetails determineAttributeJavaType( final ModelsContext modelsContext = bootstrapContext.getModelsContext(); // UserType - final JaxbUserTypeImpl userTypeNode = jaxbBasicMapping.getType(); + final var userTypeNode = jaxbBasicMapping.getType(); if ( userTypeNode != null ) { final String userTypeImplName = userTypeNode.getValue(); if ( isNotEmpty( userTypeImplName ) ) { @@ -556,10 +551,10 @@ private static ClassDetails determineAttributeJavaType( // JavaType final String javaTypeImplName = jaxbBasicMapping.getJavaType(); if ( isNotEmpty( javaTypeImplName ) ) { - final ClassDetails javaTypeImplDetails = xmlDocumentContext.resolveJavaType( javaTypeImplName ); + final var javaTypeImplDetails = xmlDocumentContext.resolveJavaType( javaTypeImplName ); // safe to convert to class, though unfortunate to have to instantiate it... final JavaType javaType = createInstance( javaTypeImplDetails ); - final Class modelClass = javaType.getJavaTypeClass(); + final var modelClass = javaType.getJavaTypeClass(); return modelsContext.getClassDetailsRegistry().getClassDetails( modelClass.getName() ); } @@ -568,7 +563,7 @@ private static ClassDetails determineAttributeJavaType( final Integer jdbcTypeCode = jaxbBasicMapping.getJdbcTypeCode(); final JdbcType jdbcType; if ( isNotEmpty( jdbcTypeImplName ) ) { - final ClassDetails jdbcTypeImplDetails = xmlDocumentContext.resolveJavaType( javaTypeImplName ); + final var jdbcTypeImplDetails = xmlDocumentContext.resolveJavaType( javaTypeImplName ); jdbcType = createInstance( jdbcTypeImplDetails ); } else if ( jdbcTypeCode != null ) { @@ -578,13 +573,13 @@ else if ( jdbcTypeCode != null ) { jdbcType = null; } if ( jdbcType != null ) { - final JavaType javaType = jdbcType.getJdbcRecommendedJavaTypeMapping( 0, 0, bootstrapContext.getTypeConfiguration() ); - final Class modelClass = javaType.getJavaTypeClass(); + final var javaType = jdbcType.getRecommendedJavaType( 0, 0, bootstrapContext.getTypeConfiguration() ); + final var modelClass = javaType.getJavaTypeClass(); return modelsContext.getClassDetailsRegistry().getClassDetails( modelClass.getName() ); } if ( jaxbBasicMapping instanceof JaxbBasicImpl jaxbBasicAttribute ) { - final TemporalType temporalType = jaxbBasicAttribute.getTemporal(); + final var temporalType = jaxbBasicAttribute.getTemporal(); if ( temporalType != null ) { return resolveTemporalJavaType( temporalType, xmlDocumentContext ); } @@ -592,10 +587,10 @@ else if ( jdbcTypeCode != null ) { final String declaringTypeName; if ( declaringType instanceof JaxbEntity jaxbEntity ) { - declaringTypeName = StringHelper.nullIfEmpty( jaxbEntity.getName() ); + declaringTypeName = nullIfEmpty( jaxbEntity.getName() ); } else if ( declaringType instanceof JaxbEmbeddable jaxbEmbeddable ) { - declaringTypeName = StringHelper.nullIfEmpty( jaxbEmbeddable.getName() ); + declaringTypeName = nullIfEmpty( jaxbEmbeddable.getName() ); } else { declaringTypeName = null; @@ -617,16 +612,16 @@ else if ( declaringType instanceof JaxbEmbeddable jaxbEmbeddable ) { * SimpleTypeInterpretation we only care about the wrapper. */ private static ClassDetails resolveBasicMappingTarget(SimpleTypeInterpretation targetInterpretation, XmlDocumentContext xmlDocumentContext) { - final ModelsContext modelsContext = xmlDocumentContext.getBootstrapContext().getModelsContext(); - final ClassDetailsRegistry classDetailsRegistry = modelsContext.getClassDetailsRegistry(); + final var modelsContext = xmlDocumentContext.getBootstrapContext().getModelsContext(); + final var classDetailsRegistry = modelsContext.getClassDetailsRegistry(); return classDetailsRegistry.resolveClassDetails( targetInterpretation.getJavaType().getName() ); } private static MutableClassDetails resolveTemporalJavaType( TemporalType temporalType, XmlDocumentContext xmlDocumentContext) { - final ModelsContext modelsContext = xmlDocumentContext.getBootstrapContext().getModelsContext(); - final MutableClassDetailsRegistry classDetailsRegistry = modelsContext.getClassDetailsRegistry().as( MutableClassDetailsRegistry.class ); + final var modelsContext = xmlDocumentContext.getBootstrapContext().getModelsContext(); + final var classDetailsRegistry = modelsContext.getClassDetailsRegistry().as( MutableClassDetailsRegistry.class ); switch ( temporalType ) { case DATE -> { return (MutableClassDetails) classDetailsRegistry.resolveClassDetails( @@ -682,8 +677,8 @@ private static TypeDetails determineAttributeJavaTypeDetails( XmlDocumentContext xmlDocumentContext) { final String target = jaxbAssociationAttribute.getTargetEntity(); if ( isNotEmpty( target ) ) { - final ModelsContext modelsContext = xmlDocumentContext.getBootstrapContext().getModelsContext(); - final ClassDetails classDetails = ModelsHelper.resolveClassDetails( + final var modelsContext = xmlDocumentContext.getBootstrapContext().getModelsContext(); + final var classDetails = ModelsHelper.resolveClassDetails( target, modelsContext.getClassDetailsRegistry(), () -> new DynamicClassDetails( @@ -711,8 +706,8 @@ private static TypeDetails determineAttributeJavaTypeDetails( XmlDocumentContext xmlDocumentContext) { // Logically this is Object, which is what we return here for now. // todo : might be nice to allow specifying a "common interface" - final ModelsContext modelsContext = xmlDocumentContext.getBootstrapContext().getModelsContext(); - final ClassDetails objectClassDetails = modelsContext.getClassDetailsRegistry().resolveClassDetails( OBJECT_CLASS_NAME ); + final var modelsContext = xmlDocumentContext.getBootstrapContext().getModelsContext(); + final var objectClassDetails = modelsContext.getClassDetailsRegistry().resolveClassDetails( OBJECT_CLASS_NAME ); return new ClassTypeDetailsImpl( objectClassDetails, TypeDetails.Kind.CLASS ); } @@ -723,7 +718,7 @@ private static TypeDetails determineAttributeJavaTypeDetails( private static TypeDetails determineAttributeJavaTypeDetails( JaxbElementCollectionImpl jaxbElementCollection, XmlDocumentContext xmlDocumentContext) { - final LimitedCollectionClassification classification = nullif( jaxbElementCollection.getClassification(), LimitedCollectionClassification.BAG ); + final var classification = nullif( jaxbElementCollection.getClassification(), LimitedCollectionClassification.BAG ); return switch ( classification ) { case BAG -> resolveCollectionType( Collection.class, xmlDocumentContext ); case LIST -> resolveCollectionType( List.class, xmlDocumentContext ); @@ -733,7 +728,7 @@ private static TypeDetails determineAttributeJavaTypeDetails( } private static TypeDetails resolveCollectionType(Class collectionType, XmlDocumentContext xmlDocumentContext) { - final ClassDetails classDetails = xmlDocumentContext + final var classDetails = xmlDocumentContext .getBootstrapContext() .getModelsContext() .getClassDetailsRegistry() diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/type/AbstractPostgreSQLStructJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/type/AbstractPostgreSQLStructJdbcType.java index e07a1483242f..d8ad146d8606 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/type/AbstractPostgreSQLStructJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/type/AbstractPostgreSQLStructJdbcType.java @@ -125,17 +125,13 @@ public EmbeddableMappingType getEmbeddableMappingType() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - if ( embeddableMappingType == null ) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Object[].class ); - } - else { - //noinspection unchecked - return (JavaType) embeddableMappingType.getMappedJavaType(); - } + return embeddableMappingType == null + ? typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Object[].class ) + : embeddableMappingType.getMappedJavaType(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/type/DB2StructJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/type/DB2StructJdbcType.java index 7966480a4de6..e0672eab7bad 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/type/DB2StructJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/type/DB2StructJdbcType.java @@ -93,17 +93,13 @@ public String getStructTypeName() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - if ( embeddableMappingType == null ) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Object[].class ); - } - else { - //noinspection unchecked - return (JavaType) embeddableMappingType.getMappedJavaType(); - } + return embeddableMappingType == null + ? typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Object[].class ) + : embeddableMappingType.getMappedJavaType(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleArrayJdbcTypeConstructor.java b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleArrayJdbcTypeConstructor.java index 9b7b358806c6..990b5a929825 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleArrayJdbcTypeConstructor.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleArrayJdbcTypeConstructor.java @@ -54,7 +54,7 @@ public JdbcType resolveType( precision = columnTypeInformation.getColumnSize(); scale = columnTypeInformation.getDecimalDigits(); } - typeName = OracleArrayJdbcType.getTypeName( elementType.getJdbcRecommendedJavaTypeMapping( + typeName = OracleArrayJdbcType.getTypeName( elementType.getRecommendedJavaType( precision, scale, typeConfiguration diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleNestedTableJdbcTypeConstructor.java b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleNestedTableJdbcTypeConstructor.java index 6bbae75b3305..a93e216dca83 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleNestedTableJdbcTypeConstructor.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleNestedTableJdbcTypeConstructor.java @@ -48,7 +48,7 @@ public JdbcType resolveType( precision = columnTypeInformation.getColumnSize(); scale = columnTypeInformation.getDecimalDigits(); } - typeName = OracleArrayJdbcType.getTypeName( elementType.getJdbcRecommendedJavaTypeMapping( + typeName = OracleArrayJdbcType.getTypeName( elementType.getRecommendedJavaType( precision, scale, typeConfiguration diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java b/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java index b132621570c6..c1ca154ec5e8 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java @@ -612,14 +612,14 @@ public Boolean convertToEntityAttribute(Boolean relationalValue) { } } - private Resolution resolution(BasicJavaType explicitJavaType, JavaType javaType) { - final JavaType basicJavaType; + private Resolution resolution(BasicJavaType explicitJavaType, JavaType javaType) { + final JavaType basicJavaType; final JdbcType jdbcType; if ( explicitJdbcTypeAccess != null ) { final var typeConfiguration = getTypeConfiguration(); jdbcType = explicitJdbcTypeAccess.apply( typeConfiguration ); basicJavaType = javaType == null && jdbcType != null - ? jdbcType.getJdbcRecommendedJavaTypeMapping( null, null, typeConfiguration ) + ? jdbcType.getRecommendedJavaType( null, null, typeConfiguration ) : javaType; } else { @@ -630,7 +630,7 @@ private Resolution resolution(BasicJavaType explicitJavaType, JavaType throw new MappingException( "Unable to determine JavaType to use : " + this ); } - if ( basicJavaType instanceof BasicJavaType castType + if ( basicJavaType instanceof BasicJavaType castType && ( !basicJavaType.getJavaTypeClass().isEnum() || enumerationStyle == null ) ) { final var context = getBuildingContext(); final var autoAppliedTypeDef = context.getTypeDefinitionRegistry().resolveAutoApplied( castType ); diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/FunctionReturnImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/FunctionReturnImpl.java index 887f9ecdc258..2de0be02215b 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/FunctionReturnImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/FunctionReturnImpl.java @@ -67,7 +67,7 @@ private OutputableType getOrmType(SharedSessionContractImplementor persistenc final var typeConfiguration = persistenceContext.getFactory().getTypeConfiguration(); final var javaType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( getJdbcTypeCode() ) - .getJdbcRecommendedJavaTypeMapping( null, null, typeConfiguration ); + .getRecommendedJavaType( null, null, typeConfiguration ); final var basicType = typeConfiguration.standardBasicTypeForJavaType( javaType.getJavaTypeClass() ); //noinspection unchecked diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractResultSetAccess.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractResultSetAccess.java index 800fe88ef3a6..2154f395915c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractResultSetAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractResultSetAccess.java @@ -123,11 +123,12 @@ public BasicType resolveType(int position, JavaType explicitJavaType, ? resolvedJdbcType : jdbcType( explicitJavaType, resolvedJdbcType, length, precision, scale, typeConfiguration ); // If there is an explicit JavaType, then prefer its recommended JDBC type - final JavaType javaType = + final var javaType = explicitJavaType == null - ? jdbcType.getJdbcRecommendedJavaTypeMapping( length, scale, typeConfiguration ) + ? jdbcType.getRecommendedJavaType( length, scale, typeConfiguration ) : explicitJavaType; - return typeConfiguration.getBasicTypeRegistry().resolve( javaType, jdbcType ); + return typeConfiguration.getBasicTypeRegistry() + .resolve( (JavaType) javaType, jdbcType ); } catch (SQLException e) { throw getSqlExceptionHelper() diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java index 619cc4e01e19..a3e72c7b9028 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java @@ -10,7 +10,6 @@ import java.util.Locale; import org.hibernate.HibernateException; -import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.ResultListTransformer; import org.hibernate.sql.exec.spi.ExecutionContext; @@ -20,7 +19,6 @@ import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.spi.EntityJavaType; -import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; import org.checkerframework.checker.nullness.qual.Nullable; @@ -150,11 +148,11 @@ public List consume( rowReader.startLoading( rowProcessingState ); RuntimeException ex = null; - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); + final var persistenceContext = session.getPersistenceContextInternal(); persistenceContext.beforeLoad(); persistenceContext.getLoadContexts().register( jdbcValuesSourceProcessingState ); try { - final JavaType domainResultJavaType = resolveDomainResultJavaType( + final var domainResultJavaType = resolveDomainResultJavaType( rowReader.getDomainResultResultJavaType(), rowReader.getResultJavaTypes(), session.getTypeConfiguration() @@ -162,7 +160,7 @@ public List consume( final boolean isEntityResultType = domainResultJavaType instanceof EntityJavaType; final int initialCollectionSize = Math.min( jdbcValues.getResultCountEstimate(), INITIAL_COLLECTION_SIZE_LIMIT ); - final Results results = createResults( isEntityResultType, domainResultJavaType, initialCollectionSize ); + final var results = createResults( isEntityResultType, domainResultJavaType, initialCollectionSize ); final int readRows = readRows( rowProcessingState, rowReader, isEntityResultType, results ); rowReader.finishUp( rowProcessingState ); jdbcValuesSourceProcessingState.finishUp( readRows > 1 ); @@ -283,11 +281,12 @@ private static int readUnique( return readRows; } + @SuppressWarnings("unchecked") //TODO: fix the unchecked casts private JavaType resolveDomainResultJavaType( Class domainResultResultJavaType, List<@Nullable JavaType> resultJavaTypes, TypeConfiguration typeConfiguration) { - final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry(); + final var javaTypeRegistry = typeConfiguration.getJavaTypeRegistry(); if ( domainResultResultJavaType != null ) { final var resultJavaType = javaTypeRegistry.resolveDescriptor( domainResultResultJavaType ); @@ -295,7 +294,6 @@ private JavaType resolveDomainResultJavaType( // so resolve the most concrete type since this type is used to determine equality of objects if ( resultJavaTypes.size() == 1 && isMoreConcrete( resultJavaType, resultJavaTypes.get( 0 ) ) ) { - //noinspection unchecked return (JavaType) resultJavaTypes.get( 0 ); } return resultJavaType; @@ -303,16 +301,12 @@ && isMoreConcrete( resultJavaType, resultJavaTypes.get( 0 ) ) ) { if ( resultJavaTypes.size() == 1 ) { final var firstJavaType = resultJavaTypes.get( 0 ); - if ( firstJavaType == null ) { - return javaTypeRegistry.getDescriptor( Object.class ); - } - else { - //noinspection unchecked - return (JavaType) firstJavaType; - } + return firstJavaType == null + ? (JavaType) javaTypeRegistry.resolveDescriptor( Object.class ) + : (JavaType) firstJavaType; } else { - return javaTypeRegistry.getDescriptor( Object[].class ); + return (JavaType) javaTypeRegistry.resolveDescriptor( Object[].class ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/CustomType.java b/hibernate-core/src/main/java/org/hibernate/type/CustomType.java index 446157f45f9a..c2b82fc313f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/CustomType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/CustomType.java @@ -93,7 +93,7 @@ public CustomType(UserType userType, String[] registrationKeys, TypeConfigura else { // create a JdbcType adapter that uses the UserType binder/extract handling jdbcType = new UserTypeJdbcTypeAdapter<>( userType, mappedJavaType ); - jdbcJavaType = jdbcType.getJdbcRecommendedJavaTypeMapping( null, null, typeConfiguration ); + jdbcJavaType = jdbcType.getRecommendedJavaType( null, null, typeConfiguration ); valueExtractor = jdbcType.getExtractor( mappedJavaType ); valueBinder = jdbcType.getBinder( mappedJavaType ); jdbcLiteralFormatter = diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java index 594b738d09e1..c248d76982de 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java @@ -150,7 +150,7 @@ default T getDefaultValue() { } /** - * Obtain the "recommended" {@link JdbcType SQL type descriptor} + * Obtain the "recommended" {@linkplain JdbcType SQL type descriptor} * for this Java type. Often, but not always, the source of this * recommendation is the JDBC specification. * diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java index 2c1e3f9b7b32..4db0436629d3 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java @@ -60,22 +60,20 @@ public JdbcType getElementJdbcType() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - final JavaType elementJavaType = - elementJdbcType.getJdbcRecommendedJavaTypeMapping( precision, scale, typeConfiguration ); + final var elementJavaType = + elementJdbcType.getRecommendedJavaType( precision, scale, typeConfiguration ); final var javaType = typeConfiguration.getJavaTypeRegistry() .resolveDescriptor( newInstance( elementJavaType.getJavaTypeClass(), 0 ).getClass() ); if ( javaType instanceof BasicPluralType ) { - //noinspection unchecked - return (JavaType) javaType; + return javaType; } else { - //noinspection unchecked - return (JavaType) javaType.createJavaType( + return javaType.createJavaType( new ParameterizedTypeImpl( javaType.getJavaTypeClass(), new Type[0], null ), typeConfiguration ); @@ -155,10 +153,10 @@ protected Object[] getArray(BasicBinder binder, ValueBinder elementBin final var underlyingJdbcType = typeConfiguration.getJdbcTypeRegistry() .getDescriptor( elementJdbcType.getDefaultSqlTypeCode() ); - final Class preferredJavaTypeClass = elementJdbcType.getPreferredJavaTypeClass( options ); - final Class elementJdbcJavaTypeClass = + final var preferredJavaTypeClass = elementJdbcType.getPreferredJavaTypeClass( options ); + final var elementJdbcJavaTypeClass = preferredJavaTypeClass == null - ? underlyingJdbcType.getJdbcRecommendedJavaTypeMapping(null, null, typeConfiguration ) + ? underlyingJdbcType.getRecommendedJavaType(null, null, typeConfiguration ) .getJavaTypeClass() : preferredJavaTypeClass; final var arrayClass = (Class) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BigIntJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BigIntJdbcType.java index ad6d87804f06..272f3ae185e2 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BigIntJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BigIntJdbcType.java @@ -34,11 +34,11 @@ public int getJdbcTypeCode() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Long.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Long.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BlobJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BlobJdbcType.java index 2f04ef74e8e5..fcd47f4c1e9b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BlobJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BlobJdbcType.java @@ -45,11 +45,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Blob.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Blob.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BooleanJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BooleanJdbcType.java index 675e7db3509f..db66220c9361 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BooleanJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/BooleanJdbcType.java @@ -43,11 +43,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Boolean.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Boolean.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DateJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DateJdbcType.java index 55afae6cfca6..e08204bc420a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DateJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DateJdbcType.java @@ -49,14 +49,14 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { final var javaTypeRegistry = typeConfiguration.getJavaTypeRegistry(); return typeConfiguration.getCurrentBaseSqlTypeIndicators().preferJdbcDatetimeTypes() - ? javaTypeRegistry.getDescriptor( Date.class ) - : javaTypeRegistry.getDescriptor( LocalDate.class ); + ? javaTypeRegistry.resolveDescriptor( Date.class ) + : javaTypeRegistry.resolveDescriptor( LocalDate.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DecimalJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DecimalJdbcType.java index 6a44c758a799..b2d3966f3cb0 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DecimalJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DecimalJdbcType.java @@ -45,11 +45,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( BigDecimal.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( BigDecimal.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DoubleJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DoubleJdbcType.java index 9836964ca3c2..a6c6a94fc4af 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DoubleJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/DoubleJdbcType.java @@ -61,11 +61,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Double.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Double.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/FloatJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/FloatJdbcType.java index b07f6cf7c43d..1e714b55c5a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/FloatJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/FloatJdbcType.java @@ -44,17 +44,17 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { if ( length != null ) { int floatPrecision = typeConfiguration.getCurrentBaseSqlTypeIndicators().getDialect().getFloatPrecision(); if ( length <= floatPrecision ) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Float.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Float.class ); } } - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Double.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Double.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/IntegerJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/IntegerJdbcType.java index 7f18e71c1217..82e877604b0f 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/IntegerJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/IntegerJdbcType.java @@ -44,11 +44,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Integer.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Integer.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcType.java index cadfedf8a669..b577d967964e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JdbcType.java @@ -90,8 +90,8 @@ default int getDefaultSqlTypeCode() { } /** - * A {@linkplain SqlTypes JDBC type code} that identifies the SQL column type to - * be used for schema generation. + * A {@linkplain SqlTypes JDBC type code} that identifies the SQL column type + * used for schema generation. *

* This value is passed to {@link DdlTypeRegistry#getTypeName(int, Size, Type)} * to obtain the SQL column type. @@ -103,21 +103,40 @@ default int getDdlTypeCode() { return getDefaultSqlTypeCode(); } - default JavaType getJdbcRecommendedJavaTypeMapping( - Integer precision, - Integer scale, + /** + * The {@linkplain JavaType Java type} usually is used to represent values of + * this JDBC type in the entity model of the data. Often, but not always, the + * source of this recommendation is the JDBC specification. + * + * @since 7.2 + */ + default JavaType getRecommendedJavaType( + Integer precision, Integer scale, TypeConfiguration typeConfiguration) { // match legacy behavior - return typeConfiguration.getJavaTypeRegistry().getDescriptor( + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( JdbcTypeJavaClassMappings.INSTANCE.determineJavaClassForJdbcTypeCode( getDefaultSqlTypeCode() ) ); } /** - * Obtain a {@linkplain JdbcLiteralFormatter formatter} object capable of rendering - * values of the given {@linkplain JavaType Java type} as SQL literals of the type - * represented by this object. + * @deprecated Due to the unchecked cast to a generic type. + * Use {@link #getRecommendedJavaType}. */ + @Deprecated(forRemoval = true, since = "7.2") + @SuppressWarnings("unchecked") + default JavaType getJdbcRecommendedJavaTypeMapping( + Integer precision, + Integer scale, + TypeConfiguration typeConfiguration) { + return (JavaType) getRecommendedJavaType( precision, scale, typeConfiguration ); + } + + /** + * Obtain a {@linkplain JdbcLiteralFormatter formatter} object capable of rendering + * values of the given {@linkplain JavaType Java type} as SQL literals of the type + * represented by this object. + */ // todo (6.0) : move to {@link org.hibernate.metamodel.mapping.JdbcMapping}? default JdbcLiteralFormatter getJdbcLiteralFormatter(JavaType javaType) { return (appender, value, dialect, wrapperOptions) -> diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/NVarcharJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/NVarcharJdbcType.java index 0ad1134cb674..34b9fef75f1a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/NVarcharJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/NVarcharJdbcType.java @@ -47,14 +47,14 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { if ( length != null && length == 1 ) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Character.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Character.class ); } - return typeConfiguration.getJavaTypeRegistry().getDescriptor( String.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( String.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/RealJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/RealJdbcType.java index 81f017dbee67..dd143137c8f7 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/RealJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/RealJdbcType.java @@ -40,11 +40,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Float.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Float.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/SmallIntJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/SmallIntJdbcType.java index 34c424082b46..8739c5560952 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/SmallIntJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/SmallIntJdbcType.java @@ -44,11 +44,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Short.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Short.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/StructJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/StructJdbcType.java index 59cfcee684f6..43a643896f26 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/StructJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/StructJdbcType.java @@ -104,17 +104,13 @@ public EmbeddableMappingType getEmbeddableMappingType() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - if ( embeddableMappingType == null ) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Object[].class ); - } - else { - //noinspection unchecked - return (JavaType) embeddableMappingType.getMappedJavaType(); - } + return embeddableMappingType == null + ? typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Object[].class ) + : embeddableMappingType.getMappedJavaType(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeAsTimestampWithTimeZoneJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeAsTimestampWithTimeZoneJdbcType.java index 0a0ab2e08eba..a8da092f901d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeAsTimestampWithTimeZoneJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeAsTimestampWithTimeZoneJdbcType.java @@ -43,11 +43,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( OffsetTime.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( OffsetTime.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeJdbcType.java index a31f17144629..cc3dba3719a3 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeJdbcType.java @@ -49,14 +49,14 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { final var javaTypeRegistry = typeConfiguration.getJavaTypeRegistry(); return typeConfiguration.getCurrentBaseSqlTypeIndicators().preferJdbcDatetimeTypes() - ? javaTypeRegistry.getDescriptor( Time.class ) - : javaTypeRegistry.getDescriptor( LocalTime.class ); + ? javaTypeRegistry.resolveDescriptor( Time.class ) + : javaTypeRegistry.resolveDescriptor( LocalTime.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeUtcAsJdbcTimeJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeUtcAsJdbcTimeJdbcType.java index 1dcd84870e45..ead44b9f8d3d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeUtcAsJdbcTimeJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeUtcAsJdbcTimeJdbcType.java @@ -59,11 +59,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( OffsetTime.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( OffsetTime.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeUtcAsOffsetTimeJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeUtcAsOffsetTimeJdbcType.java index 7f017d80b706..80871334f67a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeUtcAsOffsetTimeJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeUtcAsOffsetTimeJdbcType.java @@ -55,11 +55,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( OffsetTime.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( OffsetTime.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeWithTimeZoneJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeWithTimeZoneJdbcType.java index e4a286bb9531..301439227a94 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeWithTimeZoneJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimeWithTimeZoneJdbcType.java @@ -49,11 +49,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( OffsetTime.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( OffsetTime.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampJdbcType.java index 43f1922633c6..636d6e0eebe5 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampJdbcType.java @@ -49,14 +49,14 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { final var javaTypeRegistry = typeConfiguration.getJavaTypeRegistry(); return typeConfiguration.getCurrentBaseSqlTypeIndicators().preferJdbcDatetimeTypes() - ? javaTypeRegistry.getDescriptor( Timestamp.class ) - : javaTypeRegistry.getDescriptor( LocalDateTime.class ); + ? javaTypeRegistry.resolveDescriptor( Timestamp.class ) + : javaTypeRegistry.resolveDescriptor( LocalDateTime.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampUtcAsInstantJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampUtcAsInstantJdbcType.java index 96008c8246d6..a11557091a4f 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampUtcAsInstantJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampUtcAsInstantJdbcType.java @@ -57,11 +57,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Instant.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Instant.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampUtcAsJdbcTimestampJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampUtcAsJdbcTimestampJdbcType.java index d0871a121e94..49d220330637 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampUtcAsJdbcTimestampJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampUtcAsJdbcTimestampJdbcType.java @@ -58,11 +58,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Instant.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Instant.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampUtcAsOffsetDateTimeJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampUtcAsOffsetDateTimeJdbcType.java index 134fecc5d551..e5b41070e60a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampUtcAsOffsetDateTimeJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampUtcAsOffsetDateTimeJdbcType.java @@ -56,11 +56,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Instant.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Instant.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampWithTimeZoneJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampWithTimeZoneJdbcType.java index 4a1604c4d427..a4119ba45057 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampWithTimeZoneJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TimestampWithTimeZoneJdbcType.java @@ -48,11 +48,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( OffsetDateTime.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( OffsetDateTime.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TinyIntJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TinyIntJdbcType.java index e340be866863..63dc65e8bf4d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TinyIntJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/TinyIntJdbcType.java @@ -47,11 +47,11 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Byte.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Byte.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarbinaryJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarbinaryJdbcType.java index b8f588aa8908..e7bd6d448b40 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarbinaryJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarbinaryJdbcType.java @@ -53,11 +53,11 @@ public int getJdbcTypeCode() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( byte[].class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( byte[].class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarcharJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarcharJdbcType.java index 7015b5a2c442..b9db43dafa89 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarcharJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/VarcharJdbcType.java @@ -46,12 +46,12 @@ public String toString() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { return typeConfiguration.getJavaTypeRegistry() - .getDescriptor( length != null && length == 1 ? Character.class : String.class ); + .resolveDescriptor( length != null && length == 1 ? Character.class : String.class ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/AbstractJavaTimeJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/AbstractJavaTimeJdbcType.java index 6664fffa80da..2b859a4a1b19 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/AbstractJavaTimeJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/AbstractJavaTimeJdbcType.java @@ -29,11 +29,11 @@ public Class getPreferredJavaTypeClass(WrapperOptions options) { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( javaTimeType ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( javaTimeType ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/DelayedStructJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/DelayedStructJdbcType.java index 8cba3c394859..730082cef19a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/DelayedStructJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/internal/DelayedStructJdbcType.java @@ -51,12 +51,11 @@ public String getStructTypeName() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - //noinspection unchecked - return (JavaType) embeddableAggregateJavaType; + return embeddableAggregateJavaType; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/internal/UserTypeJdbcTypeAdapter.java b/hibernate-core/src/main/java/org/hibernate/type/internal/UserTypeJdbcTypeAdapter.java index 4b46f66b3e7f..70660b1bc5ef 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/internal/UserTypeJdbcTypeAdapter.java +++ b/hibernate-core/src/main/java/org/hibernate/type/internal/UserTypeJdbcTypeAdapter.java @@ -76,12 +76,11 @@ public ValueExtractor getExtractor(JavaType javaType) { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer length, Integer scale, TypeConfiguration typeConfiguration) { - //noinspection unchecked - return (JavaType) javaType; + return javaType; } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/inet/InetJdbcType.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/inet/InetJdbcType.java index 844ac5775c43..6a64c9e4bd22 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/inet/InetJdbcType.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/inet/InetJdbcType.java @@ -17,11 +17,11 @@ public class InetJdbcType extends PostgreSQLInetJdbcType { public static final InetJdbcType INSTANCE = new InetJdbcType(); @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( Inet.class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( Inet.class ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/array/StringArrayJdbcType.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/array/StringArrayJdbcType.java index 991bc3c33d49..30755f8ffc1f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/array/StringArrayJdbcType.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/contribution/array/StringArrayJdbcType.java @@ -29,8 +29,8 @@ public int getJdbcTypeCode() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping(Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( String[].class ); + public JavaType getRecommendedJavaType(Integer precision, Integer scale, TypeConfiguration typeConfiguration) { + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( String[].class ); } private final ValueBinder binder = new BasicBinder( StringArrayJavaType.INSTANCE, this ) { diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/HANAVectorJdbcType.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/HANAVectorJdbcType.java index 9c204d91ae71..30b1f2f5be89 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/HANAVectorJdbcType.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/HANAVectorJdbcType.java @@ -45,11 +45,11 @@ public int getDefaultSqlTypeCode() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( float[].class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( float[].class ); } @Override diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/MariaDBVectorJdbcType.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/MariaDBVectorJdbcType.java index b72cd7538c30..1f377158bb8b 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/MariaDBVectorJdbcType.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/MariaDBVectorJdbcType.java @@ -40,11 +40,11 @@ public int getDefaultSqlTypeCode() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( float[].class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( float[].class ); } @Override diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/MySQLVectorJdbcType.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/MySQLVectorJdbcType.java index 71095bfce147..94acbd20a24d 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/MySQLVectorJdbcType.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/MySQLVectorJdbcType.java @@ -43,11 +43,11 @@ public int getDefaultSqlTypeCode() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( float[].class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( float[].class ); } @Override diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGBinaryVectorJdbcType.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGBinaryVectorJdbcType.java index 6117483fab9c..49cce8bf4de5 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGBinaryVectorJdbcType.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGBinaryVectorJdbcType.java @@ -45,11 +45,11 @@ public JdbcLiteralFormatter getJdbcLiteralFormatter(JavaType javaTypeD } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( byte[].class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( byte[].class ); } // @Override diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGSparseFloatVectorJdbcType.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGSparseFloatVectorJdbcType.java index b7243e1d234f..253bf2872d01 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGSparseFloatVectorJdbcType.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGSparseFloatVectorJdbcType.java @@ -39,11 +39,11 @@ public int getDefaultSqlTypeCode() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( float[].class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( float[].class ); } @Override diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGVectorJdbcType.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGVectorJdbcType.java index 1dc0d917f228..e21498916f03 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGVectorJdbcType.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/PGVectorJdbcType.java @@ -41,11 +41,11 @@ public int getDefaultSqlTypeCode() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( float[].class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( float[].class ); } @Override diff --git a/hibernate-vector/src/main/java/org/hibernate/vector/internal/SQLServerCastingVectorJdbcType.java b/hibernate-vector/src/main/java/org/hibernate/vector/internal/SQLServerCastingVectorJdbcType.java index c496ca4a8125..228fc7ec1531 100644 --- a/hibernate-vector/src/main/java/org/hibernate/vector/internal/SQLServerCastingVectorJdbcType.java +++ b/hibernate-vector/src/main/java/org/hibernate/vector/internal/SQLServerCastingVectorJdbcType.java @@ -48,11 +48,11 @@ public int getDefaultSqlTypeCode() { } @Override - public JavaType getJdbcRecommendedJavaTypeMapping( + public JavaType getRecommendedJavaType( Integer precision, Integer scale, TypeConfiguration typeConfiguration) { - return typeConfiguration.getJavaTypeRegistry().getDescriptor( float[].class ); + return typeConfiguration.getJavaTypeRegistry().resolveDescriptor( float[].class ); } @Override From 9ce46192ab0cb3e28834f0beeec8cc1c77347364 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Tue, 2 Dec 2025 20:05:10 +0100 Subject: [PATCH 057/181] HHH-19972 Ignore PK violation when inserting just primary key columns --- .../hibernate/dialect/CockroachDialect.java | 12 ++++ .../hibernate/dialect/PostgreSQLDialect.java | 3 +- .../sql/ast/CockroachSqlAstTranslator.java | 35 ++++++++++ .../sql/ast/PostgreSQLSqlAstTranslator.java | 34 ++++++++++ .../sql/ast/spi/AbstractSqlAstTranslator.java | 6 +- .../model/internal/OptionalTableInsert.java | 41 ++++++++++++ .../jdbc/OptionalTableUpdateOperation.java | 25 +++++++- ...ptionalTableUpdateWithUpsertOperation.java | 64 +++++++++++++++++++ 8 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/model/internal/OptionalTableInsert.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateWithUpsertOperation.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index c40a5cdad6d7..210838957b0c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -51,6 +51,7 @@ import org.hibernate.exception.spi.ViolatedConstraintNameExtractor; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.config.ConfigurationHelper; +import org.hibernate.persister.entity.mutation.EntityMutationTarget; import org.hibernate.query.SemanticException; import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.sqm.IntervalType; @@ -62,6 +63,9 @@ import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; +import org.hibernate.sql.model.MutationOperation; +import org.hibernate.sql.model.internal.OptionalTableUpdate; +import org.hibernate.sql.model.jdbc.OptionalTableUpdateWithUpsertOperation; import org.hibernate.tool.schema.extract.internal.InformationExtractorPostgreSQLImpl; import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation; import org.hibernate.tool.schema.extract.spi.ExtractionContext; @@ -676,6 +680,14 @@ protected SqlAstTranslator buildTranslator( }; } + @Override + public MutationOperation createOptionalTableUpdateOperation( + EntityMutationTarget mutationTarget, + OptionalTableUpdate optionalTableUpdate, + SessionFactoryImplementor factory) { + return new OptionalTableUpdateWithUpsertOperation( mutationTarget, optionalTableUpdate, factory ); + } + @Override public NationalizationSupport getNationalizationSupport() { // TEXT / STRING inherently support nationalized data diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index 6621a58cd495..8051844e842e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -85,6 +85,7 @@ import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.model.MutationOperation; import org.hibernate.sql.model.internal.OptionalTableUpdate; +import org.hibernate.sql.model.jdbc.OptionalTableUpdateWithUpsertOperation; import org.hibernate.tool.schema.extract.internal.InformationExtractorPostgreSQLImpl; import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation; import org.hibernate.tool.schema.extract.spi.ExtractionContext; @@ -1587,7 +1588,7 @@ public MutationOperation createOptionalTableUpdateOperation( .createMergeOperation( optionalTableUpdate ); } else { - return super.createOptionalTableUpdateOperation( mutationTarget, optionalTableUpdate, factory ); + return new OptionalTableUpdateWithUpsertOperation( mutationTarget, optionalTableUpdate, factory ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/CockroachSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/CockroachSqlAstTranslator.java index 85bdacbc40ab..43bb87f4e509 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/CockroachSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/CockroachSqlAstTranslator.java @@ -27,6 +27,8 @@ import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl; import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert; +import org.hibernate.sql.model.internal.OptionalTableInsert; +import org.hibernate.sql.model.internal.TableInsertStandard; /** * A SQL AST translator for Cockroach. @@ -47,6 +49,39 @@ public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeti super.visitBinaryArithmeticExpression(arithmeticExpression); } + @Override + public void visitStandardTableInsert(TableInsertStandard tableInsert) { + getCurrentClauseStack().push( Clause.INSERT ); + try { + renderInsertInto( tableInsert ); + if ( tableInsert instanceof OptionalTableInsert optionalTableInsert ) { + appendSql( " on conflict " ); + final String constraintName = optionalTableInsert.getConstraintName(); + if ( constraintName != null ) { + appendSql( " on constraint " ); + appendSql( constraintName ); + } + else { + char separator = '('; + for ( String constraintColumnName : optionalTableInsert.getConstraintColumnNames() ) { + appendSql( separator ); + appendSql( constraintColumnName ); + separator = ','; + } + appendSql( ')' ); + } + appendSql( " do nothing" ); + } + + if ( tableInsert.getNumberOfReturningColumns() > 0 ) { + visitReturningColumns( tableInsert::getReturningColumns ); + } + } + finally { + getCurrentClauseStack().pop(); + } + } + @Override protected JdbcOperationQueryInsert translateInsert(InsertSelectStatement sqlAst) { visitInsertStatement( sqlAst ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/PostgreSQLSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/PostgreSQLSqlAstTranslator.java index da605ae88d61..f251e7aaded6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/PostgreSQLSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/sql/ast/PostgreSQLSqlAstTranslator.java @@ -32,6 +32,7 @@ import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl; import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert; +import org.hibernate.sql.model.internal.OptionalTableInsert; import org.hibernate.sql.model.internal.TableInsertStandard; import org.hibernate.type.SqlTypes; @@ -59,6 +60,39 @@ protected String getArrayContainsFunction() { return super.getArrayContainsFunction(); } + @Override + public void visitStandardTableInsert(TableInsertStandard tableInsert) { + getCurrentClauseStack().push( Clause.INSERT ); + try { + renderInsertInto( tableInsert ); + if ( tableInsert instanceof OptionalTableInsert optionalTableInsert ) { + appendSql( " on conflict " ); + final String constraintName = optionalTableInsert.getConstraintName(); + if ( constraintName != null ) { + appendSql( " on constraint " ); + appendSql( constraintName ); + } + else { + char separator = '('; + for ( String constraintColumnName : optionalTableInsert.getConstraintColumnNames() ) { + appendSql( separator ); + appendSql( constraintColumnName ); + separator = ','; + } + appendSql( ')' ); + } + appendSql( " do nothing" ); + } + + if ( tableInsert.getNumberOfReturningColumns() > 0 ) { + visitReturningColumns( tableInsert::getReturningColumns ); + } + } + finally { + getCurrentClauseStack().pop(); + } + } + @Override protected void renderInsertIntoNoColumns(TableInsertStandard tableInsert) { renderIntoIntoAndTable( tableInsert ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index 2fe0d6e35eef..1e89cb8c2072 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -188,6 +188,7 @@ import org.hibernate.sql.model.ast.ColumnWriteFragment; import org.hibernate.sql.model.ast.RestrictedTableMutation; import org.hibernate.sql.model.ast.TableMutation; +import org.hibernate.sql.model.internal.OptionalTableInsert; import org.hibernate.sql.model.internal.OptionalTableUpdate; import org.hibernate.sql.model.internal.TableDeleteCustomSql; import org.hibernate.sql.model.internal.TableDeleteStandard; @@ -8508,6 +8509,9 @@ private T translateTableMutation(TableMutation mutation) { @Override public void visitStandardTableInsert(TableInsertStandard tableInsert) { + if ( tableInsert instanceof OptionalTableInsert ) { + throw new IllegalQueryOperationException( "Optional table insert is not supported" ); + } getCurrentClauseStack().push( Clause.INSERT ); try { renderInsertInto( tableInsert ); @@ -8521,7 +8525,7 @@ public void visitStandardTableInsert(TableInsertStandard tableInsert) { } } - private void renderInsertInto(TableInsertStandard tableInsert) { + protected void renderInsertInto(TableInsertStandard tableInsert) { applySqlComment( tableInsert.getMutationComment() ); if ( tableInsert.getNumberOfValueBindings() == 0 ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/internal/OptionalTableInsert.java b/hibernate-core/src/main/java/org/hibernate/sql/model/internal/OptionalTableInsert.java new file mode 100644 index 000000000000..99d1915ea41f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/internal/OptionalTableInsert.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.sql.model.internal; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.sql.ast.tree.expression.ColumnReference; +import org.hibernate.sql.model.MutationTarget; +import org.hibernate.sql.model.ast.ColumnValueBinding; +import org.hibernate.sql.model.ast.ColumnValueParameter; +import org.hibernate.sql.model.ast.MutatingTableReference; + +import java.util.List; + +public class OptionalTableInsert extends TableInsertStandard { + + private final @Nullable String constraintName; + private final List constraintColumnNames; + + public OptionalTableInsert( + MutatingTableReference mutatingTable, + MutationTarget mutationTarget, + List valueBindings, + List returningColumns, + List parameters, + @Nullable String constraintName, + List constraintColumnNames) { + super( mutatingTable, mutationTarget, valueBindings, returningColumns, parameters ); + this.constraintName = constraintName; + this.constraintColumnNames = constraintColumnNames; + } + + public @Nullable String getConstraintName() { + return constraintName; + } + + public List getConstraintColumnNames() { + return constraintColumnNames; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateOperation.java b/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateOperation.java index 7978e09a8080..01614f4f1ca8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateOperation.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateOperation.java @@ -105,6 +105,22 @@ public TableMapping getTableDetails() { return tableMapping; } + public List getValueBindings() { + return valueBindings; + } + + public List getKeyBindings() { + return keyBindings; + } + + public List getOptimisticLockBindings() { + return optimisticLockBindings; + } + + public List getParameters() { + return parameters; + } + @Override public JdbcValueDescriptor findValueDescriptor(String columnName, ParameterUsage usage) { for ( int i = 0; i < jdbcValueDescriptors.size(); i++ ) { @@ -375,7 +391,7 @@ protected JdbcMutationOperation createJdbcUpdate(SharedSessionContractImplemento } private void performInsert(JdbcValueBindings jdbcValueBindings, SharedSessionContractImplementor session) { - final JdbcInsertMutation jdbcInsert = createJdbcInsert( session ); + final JdbcMutationOperation jdbcInsert = createJdbcOptionalInsert( session ); final JdbcServices jdbcServices = session.getJdbcServices(); final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator(); final PreparedStatement insertStatement = createStatementDetails( jdbcInsert, jdbcCoordinator ); @@ -413,6 +429,13 @@ private void performInsert(JdbcValueBindings jdbcValueBindings, SharedSessionCon } } + /* + * Used by Hibernate Reactive + */ + protected JdbcMutationOperation createJdbcOptionalInsert(SharedSessionContractImplementor session) { + return createJdbcInsert( session ); + } + /* * Used by Hibernate Reactive */ diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateWithUpsertOperation.java b/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateWithUpsertOperation.java new file mode 100644 index 000000000000..af5c148f4191 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateWithUpsertOperation.java @@ -0,0 +1,64 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.sql.model.jdbc; + +import org.hibernate.engine.jdbc.mutation.internal.MutationQueryOptions; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.util.collections.CollectionHelper; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.mutation.EntityMutationTarget; +import org.hibernate.sql.model.ast.MutatingTableReference; +import org.hibernate.sql.model.ast.TableMutation; +import org.hibernate.sql.model.internal.OptionalTableInsert; +import org.hibernate.sql.model.internal.OptionalTableUpdate; + +import java.util.Arrays; +import java.util.Collections; + +/** + * Uses {@link org.hibernate.sql.model.internal.OptionalTableInsert} for the insert operation, + * to avoid primary key constraint violations when inserting only primary key columns. + */ +public class OptionalTableUpdateWithUpsertOperation extends OptionalTableUpdateOperation { + + public OptionalTableUpdateWithUpsertOperation( + EntityMutationTarget mutationTarget, + OptionalTableUpdate upsert, + @SuppressWarnings("unused") SessionFactoryImplementor factory) { + super( mutationTarget, upsert, factory ); + } + + @Override + protected JdbcMutationOperation createJdbcOptionalInsert(SharedSessionContractImplementor session) { + if ( getTableDetails().getInsertDetails() != null + && getTableDetails().getInsertDetails().getCustomSql() != null + || !getValueBindings().isEmpty() ) { + return super.createJdbcOptionalInsert( session ); + } + else { + // Ignore a primary key violation on insert when inserting just the primary key columns + final TableMutation tableInsert = new OptionalTableInsert( + new MutatingTableReference( getTableDetails() ), + getMutationTarget(), + CollectionHelper.combine( getValueBindings(), getKeyBindings() ), + Collections.emptyList(), + getParameters(), + null, + Arrays.asList( ((EntityPersister) getMutationTarget()).getIdentifierColumnNames() ) + ); + + final SessionFactoryImplementor factory = session.getSessionFactory(); + return factory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory() + .buildModelMutationTranslator( tableInsert, factory ) + .translate( null, MutationQueryOptions.INSTANCE ); + } + } + + @Override + public String toString() { + return "OptionalTableUpdateWithUpsertOperation(" + getTableDetails() + ")"; + } +} From 06bc35a3f99f37ec459a768f181596ed825f9820 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 30 Nov 2025 22:16:27 +0100 Subject: [PATCH 058/181] first phase of strict checking of arguments to query parameters --- .../internal/ProcedureParamBindings.java | 10 +- .../query/QueryArgumentTypeException.java | 31 ++ .../internal/QueryParameterBindingsImpl.java | 11 +- .../spi/AbstractCommonQueryContract.java | 409 +++++++++++++----- .../query/spi/QueryParameterBindings.java | 12 +- .../tree/expression/SqmNamedParameter.java | 2 +- .../type/descriptor/java/EnumJavaType.java | 15 +- .../orm/test/hql/ParameterIsNullTest.java | 2 +- .../mutation/multitable/IdSelectionTests.java | 16 +- .../type/LongListTypeContributorTest.java | 2 +- 10 files changed, 362 insertions(+), 148 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/query/QueryArgumentTypeException.java diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParamBindings.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParamBindings.java index 95ed5c205ac4..6c2deaf960c6 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParamBindings.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParamBindings.java @@ -75,23 +75,21 @@ public

ProcedureParameterBinding

getQueryParameterBinding(ProcedureParame } @Override - public

ProcedureParameterBinding

getBinding(String name) { + public ProcedureParameterBinding getBinding(String name) { final var parameter = parameterMetadata.getQueryParameter( name ); if ( parameter == null ) { throw new IllegalArgumentException( "Parameter with name '" + name + "' does not exist" ); } - //noinspection unchecked - return getQueryParameterBinding( (ProcedureParameterImplementor

) parameter ); + return getQueryParameterBinding( (ProcedureParameterImplementor) parameter ); } @Override - public

ProcedureParameterBinding

getBinding(int position) { + public ProcedureParameterBinding getBinding(int position) { final var parameter = parameterMetadata.getQueryParameter( position ); if ( parameter == null ) { throw new IllegalArgumentException( "Parameter at position " + position + "does not exist" ); } - //noinspection unchecked - return getQueryParameterBinding( (ProcedureParameterImplementor

) parameter ); + return getQueryParameterBinding( (ProcedureParameterImplementor) parameter ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/QueryArgumentTypeException.java b/hibernate-core/src/main/java/org/hibernate/query/QueryArgumentTypeException.java new file mode 100644 index 000000000000..150d782cdbd3 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/QueryArgumentTypeException.java @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.query; + +/** + * Thrown when an argument bound to a query parameter is of an incompatible type. + * + * @since 7.2 + * + * @author Gavin King + */ +public class QueryArgumentTypeException extends IllegalArgumentException { + private final Class expectedType; + private final Class actualType; + + public QueryArgumentTypeException(String message, Class expectedType, Class actualType) { + super( message + " (" + actualType.getName() + " is not assignable to " + expectedType.getName() + ")" ); + this.expectedType = expectedType; + this.actualType = actualType; + } + + public Class getExpectedType() { + return expectedType; + } + + public Class getActualType() { + return actualType; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java index 281a0b5de19e..2561fd7abd5e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java @@ -129,30 +129,29 @@ public

QueryParameterBinding

getBinding(QueryParameterImplementor

para "Cannot create binding for parameter reference [" + parameter + "] - reference is not a parameter of this query" ); } + //TODO: typecheck! //noinspection unchecked return (QueryParameterBinding

) binding; } @Override - public

QueryParameterBinding

getBinding(int position) { + public QueryParameterBinding getBinding(int position) { final var binding = parameterBindingMapByNameOrPosition.get( position ); if ( binding == null ) { // Invoke this method to throw the exception parameterMetadata.getQueryParameter( position ); } - //noinspection unchecked - return (QueryParameterBinding

) binding; + return binding; } @Override - public

QueryParameterBinding

getBinding(String name) { + public QueryParameterBinding getBinding(String name) { final var binding = parameterBindingMapByNameOrPosition.get( name ); if ( binding == null ) { // Invoke this method to throw the exception parameterMetadata.getQueryParameter( name ); } - //noinspection unchecked - return (QueryParameterBinding

) binding; + return binding; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java index b6678db8e43c..d42736c53afd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java @@ -35,10 +35,10 @@ import org.hibernate.query.QueryFlushMode; import org.hibernate.query.QueryParameter; import org.hibernate.query.TypedParameterValue; +import org.hibernate.query.QueryArgumentTypeException; import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.internal.QueryOptionsImpl; import org.hibernate.query.sqm.NodeBuilder; -import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.tree.expression.SqmLiteral; import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; @@ -51,10 +51,12 @@ import java.util.Collection; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; -import static java.util.Arrays.asList; +import static java.lang.Math.round; +import static java.util.Collections.unmodifiableSet; import static java.util.Locale.ROOT; import static org.hibernate.internal.log.DeprecationLogger.DEPRECATION_LOGGER; import static org.hibernate.jpa.HibernateHints.HINT_CACHEABLE; @@ -282,7 +284,7 @@ public final boolean applyHint(String hintName, Object value) { //fall through: case HINT_SPEC_QUERY_TIMEOUT: // convert milliseconds to seconds - int timeout = (int) Math.round( getInteger( value ).doubleValue() / 1000.0 ); + final int timeout = (int) round( getInteger( value ).doubleValue() / 1000.0 ); applyTimeoutHint( timeout ); return true; case HINT_COMMENT: @@ -621,15 +623,13 @@ public int getFirstResult() { @Override public abstract ParameterMetadataImplementor getParameterMetadata(); - @SuppressWarnings({"unchecked", "rawtypes"}) public Set> getParameters() { checkOpenNoRollback(); - return (Set) getParameterMetadata().getRegistrations(); + return unmodifiableSet( getParameterMetadata().getRegistrations() ); } public QueryParameterImplementor getParameter(String name) { checkOpenNoRollback(); - try { return getParameterMetadata().getQueryParameter( name ); } @@ -638,21 +638,20 @@ public QueryParameterImplementor getParameter(String name) { } } - @SuppressWarnings("unchecked") public QueryParameterImplementor getParameter(String name, Class type) { checkOpenNoRollback(); - try { - //noinspection rawtypes - final QueryParameterImplementor parameter = getParameterMetadata().getQueryParameter( name ); + final var parameter = getParameterMetadata().getQueryParameter( name ); if ( !type.isAssignableFrom( parameter.getParameterType() ) ) { - throw new IllegalArgumentException( - "The type [" + parameter.getParameterType().getName() + - "] associated with the parameter corresponding to name [" + name + - "] is not assignable to requested Java type [" + type.getName() + "]" + throw new QueryArgumentTypeException( + "Type specified for parameter named '" + name + "' is incompatible", + parameter.getParameterType(), + type ); } - return parameter; + @SuppressWarnings("unchecked") // safe, just checked + var castParameter = (QueryParameterImplementor) parameter; + return castParameter; } catch ( HibernateException e ) { throw getExceptionConverter().convert( e ); @@ -661,7 +660,6 @@ public QueryParameterImplementor getParameter(String name, Class type) public QueryParameterImplementor getParameter(int position) { checkOpenNoRollback(); - try { return getParameterMetadata().getQueryParameter( position ); } @@ -670,20 +668,20 @@ public QueryParameterImplementor getParameter(int position) { } } - @SuppressWarnings( {"unchecked", "rawtypes"} ) public QueryParameterImplementor getParameter(int position, Class type) { checkOpenNoRollback(); - try { - final QueryParameterImplementor parameter = getParameterMetadata().getQueryParameter( position ); + final var parameter = getParameterMetadata().getQueryParameter( position ); if ( !type.isAssignableFrom( parameter.getParameterType() ) ) { - throw new IllegalArgumentException( - "The type [" + parameter.getParameterType().getName() + - "] associated with the parameter corresponding to position [" + position + - "] is not assignable to requested Java type [" + type.getName() + "]" + throw new QueryArgumentTypeException( + "Type specified for parameter at position " + position + " is incompatible", + parameter.getParameterType(), + type ); } - return parameter; + @SuppressWarnings("unchecked") // safe, just checked + var castParameter = (QueryParameterImplementor) parameter; + return castParameter; } catch ( HibernateException e ) { throw getExceptionConverter().convert( e ); @@ -703,15 +701,31 @@ private

JavaType

getJavaType(Class

javaType) { .resolveDescriptor( javaType ); } - protected

QueryParameterBinding

locateBinding(Parameter

parameter) { + protected

QueryParameterBinding

locateBinding(Parameter

parameter, P value) { if ( parameter instanceof QueryParameterImplementor

parameterImplementor ) { return locateBinding( parameterImplementor ); } else if ( parameter.getName() != null ) { - return locateBinding( parameter.getName() ); + return locateBinding( parameter.getName(), parameter.getParameterType(), value ); } else if ( parameter.getPosition() != null ) { - return locateBinding( parameter.getPosition() ); + return locateBinding( parameter.getPosition(), parameter.getParameterType(), value ); + } + + throw getExceptionConverter().convert( + new IllegalArgumentException( "Could not resolve binding for given parameter reference [" + parameter + "]" ) + ); + } + + protected

QueryParameterBinding

locateBinding(Parameter

parameter, Collection values) { + if ( parameter instanceof QueryParameterImplementor

parameterImplementor ) { + return locateBinding( parameterImplementor ); + } + else if ( parameter.getName() != null ) { + return locateBinding( parameter.getName(), parameter.getParameterType(), values ); + } + else if ( parameter.getPosition() != null ) { + return locateBinding( parameter.getPosition(), parameter.getParameterType(), values ); } throw getExceptionConverter().convert( @@ -724,14 +738,76 @@ protected

QueryParameterBinding

locateBinding(QueryParameterImplementor

QueryParameterBinding

locateBinding(String name) { + protected

QueryParameterBinding

locateBinding(String name, Class

javaType, P value) { + getCheckOpen(); + final var binding = getQueryParameterBindings().getBinding( name ); + final var parameterType = binding.getBindType(); + if ( parameterType != null ) { + final var parameterJavaType = parameterType.getJavaType(); + if ( !parameterJavaType.isAssignableFrom( javaType ) + && !isInstance( parameterType, value ) ) { + throw new QueryArgumentTypeException( + "Argument to parameter named '" + name + "' has an incompatible type", + parameterJavaType, javaType); + } + } + @SuppressWarnings("unchecked") // safe, just checked + var castBinding = (QueryParameterBinding

) binding; + return castBinding; + } + + protected

QueryParameterBinding

locateBinding(int position, Class

javaType, P value) { + getCheckOpen(); + final var binding = getQueryParameterBindings().getBinding( position ); + final var parameterType = binding.getBindType(); + if ( parameterType != null ) { + final var parameterJavaType = parameterType.getJavaType(); + if ( !parameterJavaType.isAssignableFrom( javaType ) + && !isInstance( parameterType, value ) ) { + throw new QueryArgumentTypeException( + "Argument to parameter at position " + position + " has an incompatible type", + parameterJavaType, javaType ); + } + } + @SuppressWarnings("unchecked") // safe, just checked + var castBinding = (QueryParameterBinding

) binding; + return castBinding; + } + + protected

QueryParameterBinding

locateBinding(String name, Class

javaType, Collection values) { getCheckOpen(); - return getQueryParameterBindings().getBinding( name ); + final var binding = getQueryParameterBindings().getBinding( name ); + final var parameterType = binding.getBindType(); + if ( parameterType != null ) { + final var parameterJavaType = parameterType.getJavaType(); + if ( !parameterJavaType.isAssignableFrom( javaType ) + && !areInstances( parameterType, values ) ) { + throw new QueryArgumentTypeException( + "Argument to parameter named '" + name + "' has an incompatible type", + parameterJavaType, javaType); + } + } + @SuppressWarnings("unchecked") // safe, just checked + var castBinding = (QueryParameterBinding

) binding; + return castBinding; } - protected

QueryParameterBinding

locateBinding(int position) { + protected

QueryParameterBinding

locateBinding(int position, Class

javaType, Collection values) { getCheckOpen(); - return getQueryParameterBindings().getBinding( position ); + final var binding = getQueryParameterBindings().getBinding( position ); + final var parameterType = binding.getBindType(); + if ( parameterType != null ) { + final var parameterJavaType = parameterType.getJavaType(); + if ( !parameterJavaType.isAssignableFrom( javaType ) + && !areInstances( parameterType, values ) ) { + throw new QueryArgumentTypeException( + "Argument to parameter at position " + position + " has an incompatible type", + parameterJavaType, javaType ); + } + } + @SuppressWarnings("unchecked") // safe, just checked + var castBinding = (QueryParameterBinding

) binding; + return castBinding; } protected

QueryParameterImplementor

getQueryParameter(QueryParameterImplementor

parameter) { @@ -741,22 +817,22 @@ protected

QueryParameterImplementor

getQueryParameter(QueryParameterImple public boolean isBound(Parameter param) { getCheckOpen(); final var parameter = getParameterMetadata().resolve( param ); - return parameter != null && getQueryParameterBindings().isBound( getQueryParameter( parameter ) ); + return parameter != null + && getQueryParameterBindings().isBound( getQueryParameter( parameter ) ); } public T getParameterValue(Parameter param) { checkOpenNoRollback(); - final var parameter = getParameterMetadata().resolve( param ); if ( parameter == null ) { throw new IllegalArgumentException( "The parameter [" + param + "] is not part of this Query" ); } - - final var binding = getQueryParameterBindings().getBinding( getQueryParameter( parameter ) ); + final var binding = + getQueryParameterBindings() + .getBinding( getQueryParameter( parameter ) ); if ( binding == null || !binding.isBound() ) { throw new IllegalStateException( "Parameter value not yet bound : " + param.toString() ); } - if ( binding.isMultiValued() ) { // TODO: THIS IS UNSOUND //noinspection unchecked @@ -769,54 +845,69 @@ public T getParameterValue(Parameter param) { public Object getParameterValue(String name) { checkOpenNoRollback(); - - final QueryParameterBinding binding = getQueryParameterBindings().getBinding( name ); + final var binding = getQueryParameterBindings().getBinding( name ); if ( !binding.isBound() ) { throw new IllegalStateException( "The parameter [" + name + "] has not yet been bound" ); } - - if ( binding.isMultiValued() ) { - return binding.getBindValues(); - } - else { - return binding.getBindValue(); - } + return binding.isMultiValued() ? binding.getBindValues() : binding.getBindValue(); } public Object getParameterValue(int position) { checkOpenNoRollback(); - - final QueryParameterBinding binding = getQueryParameterBindings().getBinding( position ); + final var binding = getQueryParameterBindings().getBinding( position ); if ( !binding.isBound() ) { throw new IllegalStateException( "The parameter [" + position + "] has not yet been bound" ); } - return binding.isMultiValued() ? binding.getBindValues() : binding.getBindValue(); } + + private

void setParameterValue(Object value, QueryParameterBinding

binding) { + final var parameterType = binding.getBindType(); + if ( parameterType != null + && !isInstanceOrAreInstances( value, binding, parameterType ) ) { + throw new QueryArgumentTypeException( "Argument to query parameter has an incompatible type", + parameterType.getJavaType(), value.getClass() ); + } + @SuppressWarnings("unchecked") // safe, just checked + final var castValue = (P) value; + binding.setBindValue( castValue, resolveJdbcParameterTypeIfNecessary() ); + } + + private

boolean isInstanceOrAreInstances( + Object value, QueryParameterBinding

binding, BindableType parameterType) { + return binding.isMultiValued() && value instanceof Collection values + ? areInstances( parameterType, values ) + : isInstance( parameterType, value ); + } + @Override public CommonQueryContract setParameter(String name, Object value) { checkOpenNoRollback(); - if ( value instanceof TypedParameterValue typedParameterValue ) { setTypedParameter( name, typedParameterValue ); } else { - final QueryParameterBinding binding = getQueryParameterBindings().getBinding( name ); - if ( multipleBinding( binding.getQueryParameter(), value ) - && value instanceof Collection collectionValue - && !isRegisteredAsBasicType( value.getClass() ) ) { - return setParameterList( name, collectionValue ); + final var binding = getQueryParameterBindings().getBinding( name ); + if ( multipleBinding( binding.getQueryParameter(), value ) ) { + setParameterList( name, (Collection) value ); + } + else { + setParameterValue( value, binding ); } - binding.setBindValue( value, resolveJdbcParameterTypeIfNecessary() ); } - return this; } - private boolean multipleBinding(QueryParameter parameter, Object value){ - if ( parameter.allowsMultiValuedBinding() ) { + private boolean multipleBinding(QueryParameter parameter, Object value){ + if ( parameter.allowsMultiValuedBinding() + && value instanceof Collection values + // this check only needed for some native queries + && !isRegisteredAsBasicType( value.getClass() ) ) { final var hibernateType = parameter.getHibernateType(); - return hibernateType == null || value == null || isInstance( hibernateType, value ); + return hibernateType == null + || values.isEmpty() + || !isInstance( hibernateType, value ) + || isInstance( hibernateType, values.iterator().next() ); } else { return false; @@ -832,12 +923,67 @@ private void setTypedParameter(int position, TypedParameterValue typedVal } private boolean isInstance(Type parameterType, Object value) { - final SqmExpressible sqmExpressible = getNodeuilder().resolveExpressible( parameterType ); + if ( value == null ) { + return true; + } + final var sqmExpressible = getNodeBuilder().resolveExpressible( parameterType ); + assert sqmExpressible != null; + final var javaType = sqmExpressible.getExpressibleJavaType(); + if ( !javaType.isInstance( value ) ) { + try { + // if this succeeds, we are good + javaType.wrap( value, session ); + } + catch ( HibernateException|UnsupportedOperationException e) { + return false; + } + } + return true; + } + + private boolean areInstances(Type parameterType, Collection values) { + if ( values.isEmpty() ) { + return true; + } + final var sqmExpressible = getNodeBuilder().resolveExpressible( parameterType ); assert sqmExpressible != null; - return sqmExpressible.getExpressibleJavaType().isInstance( value ); + final var javaType = sqmExpressible.getExpressibleJavaType(); + for ( Object value : values ) { + if ( !javaType.isInstance( value ) ) { + try { + // if this succeeds, we are good + javaType.wrap( value, session ); + } + catch (HibernateException | UnsupportedOperationException e) { + return false; + } + } + } + return true; + } + + private boolean areInstances(Type parameterType, Object[] values) { + if ( values.length == 0 ) { + return true; + } + final var sqmExpressible = getNodeBuilder().resolveExpressible( parameterType ); + assert sqmExpressible != null; + final var javaType = sqmExpressible.getExpressibleJavaType(); + for ( Object value : values ) { + if ( !javaType.isInstance( value ) ) { + try { + // if this succeeds, we are good + javaType.wrap( value, session ); + } + catch (HibernateException | UnsupportedOperationException e) { + return false; + } + } + } + return true; } - private NodeBuilder getNodeuilder() { + private NodeBuilder getNodeBuilder() { return getSessionFactory().getQueryEngine().getCriteriaBuilder(); } @@ -855,31 +1001,30 @@ public

CommonQueryContract setParameter(String name, P value, Class

javaT @Override public

CommonQueryContract setParameter(String name, P value, Type

type) { - this.

locateBinding( name ).setBindValue( value, (BindableType

) type ); + locateBinding( name, type.getJavaType(), value ).setBindValue( value, (BindableType

) type ); return this; } @Override @Deprecated(since = "7") public CommonQueryContract setParameter(String name, Instant value, TemporalType temporalType) { - this.locateBinding( name ).setBindValue( value, temporalType ); + locateBinding( name, Instant.class, value ).setBindValue( value, temporalType ); return this; } @Override public CommonQueryContract setParameter(int position, Object value) { checkOpenNoRollback(); - if ( value instanceof TypedParameterValue typedParameterValue ) { setTypedParameter( position, typedParameterValue ); } else { - final QueryParameterBinding binding = getQueryParameterBindings().getBinding( position ); - if ( multipleBinding( binding.getQueryParameter(), value ) - && value instanceof Collection collectionValue - && !isRegisteredAsBasicType( value.getClass() ) ) { - return setParameterList( position, collectionValue ); + final var binding = getQueryParameterBindings().getBinding( position ); + if ( multipleBinding( binding.getQueryParameter(), value ) ) { + setParameterList( position, (Collection) value ); + } + else { + setParameterValue( value, binding ); } - binding.setBindValue( value, resolveJdbcParameterTypeIfNecessary() ); } return this; } @@ -902,19 +1047,20 @@ public

CommonQueryContract setParameter(int position, P value, Class

java @Override public

CommonQueryContract setParameter(int position, P value, Type

type) { - this.

locateBinding( position ).setBindValue( value, (BindableType

) type ); + locateBinding( position, type.getJavaType(), value ).setBindValue( value, (BindableType

) type ); return this; } @Override @Deprecated(since = "7") public CommonQueryContract setParameter(int position, Instant value, TemporalType temporalType) { - this.locateBinding( position ).setBindValue( value, temporalType ); + locateBinding( position, Instant.class, value ).setBindValue( value, temporalType ); return this; } @Override public

CommonQueryContract setParameter(QueryParameter

parameter, P value) { - locateBinding( parameter ).setBindValue( value, resolveJdbcParameterTypeIfNecessary() ); + locateBinding( parameter, value ) + .setBindValue( value, resolveJdbcParameterTypeIfNecessary() ); return this; } @@ -932,7 +1078,8 @@ public

CommonQueryContract setParameter(QueryParameter

parameter, P value @Override public

CommonQueryContract setParameter(QueryParameter

parameter, P value, Type

type) { - locateBinding( parameter ).setBindValue( value, (BindableType

) type ); + locateBinding( parameter, value ) + .setBindValue( value, (BindableType

) type ); return this; } @@ -951,75 +1098,67 @@ public

CommonQueryContract setParameter(Parameter

parameter, P value) { } @SuppressWarnings("unchecked") // safe, because we just checked final var typedValue = (TypedParameterValue

) value; - setParameter( parameter, typedValue.value(), typedValue.type() ); + final var castValue = typedValue.value(); + final var castType = typedValue.type(); + if ( parameter instanceof QueryParameter

queryParameter ) { + setParameter( queryParameter, castValue, castType ); + } + else { + locateBinding( parameter, castValue ) + .setBindValue( castValue, castType ); + } } else { - locateBinding( parameter ).setBindValue( value, resolveJdbcParameterTypeIfNecessary() ); + locateBinding( parameter, value ) + .setBindValue( value, resolveJdbcParameterTypeIfNecessary() ); } return this; } - private

void setParameter(Parameter

parameter, P value, Type

type) { - if ( parameter instanceof QueryParameter

queryParameter ) { - setParameter( queryParameter, value, type ); - } - else if ( value == null ) { - locateBinding( parameter ).setBindValue( null, (BindableType

) type ); - } - else if ( value instanceof Collection ) { - //TODO: this looks wrong to me: how can value be both a P and a (Collection

)? - locateBinding( parameter ).setBindValues( (Collection

) value ); - } - else { - locateBinding( parameter ).setBindValue( value, (BindableType

) type ); - } - } - - @Override @Deprecated public CommonQueryContract setParameter(Parameter param, Calendar value, TemporalType temporalType) { - locateBinding( param ).setBindValue( value, temporalType ); + locateBinding( param, value ).setBindValue( value, temporalType ); return this; } @Override @Deprecated public CommonQueryContract setParameter(Parameter param, Date value, TemporalType temporalType) { - locateBinding( param ).setBindValue( value, temporalType ); + locateBinding( param, value ).setBindValue( value, temporalType ); return this; } @Override @Deprecated public CommonQueryContract setParameter(String name, Calendar value, TemporalType temporalType) { - locateBinding( name ).setBindValue( value, temporalType ); + locateBinding( name, Calendar.class, value ).setBindValue( value, temporalType ); return this; } @Override @Deprecated public CommonQueryContract setParameter(String name, Date value, TemporalType temporalType) { - locateBinding( name ).setBindValue( value, temporalType ); + locateBinding( name, Date.class, value ).setBindValue( value, temporalType ); return this; } @Override @Deprecated public CommonQueryContract setParameter(int position, Calendar value, TemporalType temporalType) { - locateBinding( position ).setBindValue( value, temporalType ); + locateBinding( position, Calendar.class, value ).setBindValue( value, temporalType ); return this; } @Override @Deprecated public CommonQueryContract setParameter(int position, Date value, TemporalType temporalType) { - locateBinding( position ).setBindValue( value, temporalType ); + locateBinding( position, Date.class, value ).setBindValue( value, temporalType ); return this; } @Override public CommonQueryContract setParameterList(String name, @SuppressWarnings("rawtypes") Collection values) { - locateBinding( name ).setBindValues( values ); + getQueryParameterBindings().getBinding( name ).setBindValues( values ); return this; } public

CommonQueryContract setParameterList(String name, Collection values, Class

javaType) { - final JavaType

javaDescriptor = getJavaType( javaType ); + final var javaDescriptor = getJavaType( javaType ); if ( javaDescriptor == null ) { setParameterList( name, values ); } @@ -1032,16 +1171,35 @@ public

CommonQueryContract setParameterList(String name, Collection CommonQueryContract setParameterList(String name, Collection values, Type

type) { - this.

locateBinding( name ).setBindValues( values, (BindableType

) type ); + locateBinding( name, type.getJavaType(), values ) + .setBindValues( values, (BindableType

) type ); return this; } @Override public CommonQueryContract setParameterList(String name, Object[] values) { - locateBinding( name ).setBindValues( asList( values ) ); + final var binding = getQueryParameterBindings().getBinding( name ); + setParameterValues( values, binding ); return this; } + private

void setParameterValues(Object[] values, QueryParameterBinding

binding) { + final var parameterType = binding.getBindType(); + if ( parameterType != null + && !areInstances( values, parameterType ) ) { + throw new QueryArgumentTypeException( "Argument to query parameter has an incompatible type", + parameterType.getJavaType(), values.getClass().getComponentType() ); + } + @SuppressWarnings("unchecked") // safe, just checked + final var castArray = (P[]) values; + binding.setBindValues( List.of( castArray ) ); + } + + private

boolean areInstances(Object[] values, BindableType parameterType) { + return parameterType.getJavaType().isAssignableFrom( values.getClass().getComponentType() ) + || areInstances( parameterType, values ); + } + @Override public

CommonQueryContract setParameterList(String name, P[] values, Class

javaType) { final var javaDescriptor = getJavaType( javaType ); @@ -1055,13 +1213,16 @@ public

CommonQueryContract setParameterList(String name, P[] values, Class

CommonQueryContract setParameterList(String name, P[] values, Type

type) { - this.

locateBinding( name ).setBindValues( asList( values ), (BindableType

) type ); + final var list = List.of( values ); + locateBinding( name, type.getJavaType(), list ) + .setBindValues( list, (BindableType

) type ); return this; } @Override public CommonQueryContract setParameterList(int position, @SuppressWarnings("rawtypes") Collection values) { - locateBinding( position ).setBindValues( values ); + //TODO: type checking? + getQueryParameterBindings().getBinding( position ).setBindValues( values ); return this; } @@ -1099,13 +1260,15 @@ private

Type

getParamType(Class

javaType) { @Override public

CommonQueryContract setParameterList(int position, Collection values, Type

type) { - this.

locateBinding( position ).setBindValues( values, (BindableType

) type ); + locateBinding( position, type.getJavaType(), values ) + .setBindValues( values, (BindableType

) type ); return this; } @Override public CommonQueryContract setParameterList(int position, Object[] values) { - locateBinding( position ).setBindValues( asList( values ) ); + final var binding = getQueryParameterBindings().getBinding( position ); + setParameterValues( values, binding ); return this; } @@ -1118,20 +1281,20 @@ public

CommonQueryContract setParameterList(int position, P[] values, Class< else { setParameterList( position, values, getParamType( javaType ) ); } - return this; } - @SuppressWarnings({ "unchecked", "rawtypes" }) public

CommonQueryContract setParameterList(int position, P[] values, Type

type) { - locateBinding( position ).setBindValues( asList( values ), (BindableType) type ); + final var list = List.of( values ); + locateBinding( position, type.getJavaType(), list ) + .setBindValues( list, (BindableType

) type ); return this; } @Override public

CommonQueryContract setParameterList(QueryParameter

parameter, Collection values) { - locateBinding( parameter ).setBindValues( values ); + locateBinding( parameter, values ).setBindValues( values ); return this; } @@ -1149,13 +1312,15 @@ public

CommonQueryContract setParameterList(QueryParameter

parameter, Col @Override public

CommonQueryContract setParameterList(QueryParameter

parameter, Collection values, Type

type) { - locateBinding( parameter ).setBindValues( values, (BindableType

) type ); + locateBinding( parameter, values ) + .setBindValues( values, (BindableType

) type ); return this; } @Override public

CommonQueryContract setParameterList(QueryParameter

parameter, P[] values) { - locateBinding( parameter ).setBindValues( values == null ? null : asList( values ) ); + final var list = List.of( values ); + locateBinding( parameter, list ).setBindValues( list ); return this; } @@ -1168,13 +1333,14 @@ public

CommonQueryContract setParameterList(QueryParameter

parameter, P[] else { setParameterList( parameter, values, getParamType( javaType ) ); } - return this; } @Override public

CommonQueryContract setParameterList(QueryParameter

parameter, P[] values, Type

type) { - locateBinding( parameter ).setBindValues( asList( values ), (BindableType

) type ); + final var list = List.of( values ); + locateBinding( parameter, list ) + .setBindValues( list, (BindableType

) type ); return this; } @@ -1184,7 +1350,8 @@ public CommonQueryContract setProperties(@SuppressWarnings("rawtypes") Map map) final Object object = map.get( paramName ); if ( object == null ) { if ( map.containsKey( paramName ) ) { - setParameter( paramName, null, determineType( paramName, null ) ); + setParameter( paramName, null, + determineType( paramName, null ) ); } } else { @@ -1195,7 +1362,8 @@ else if ( object instanceof Object[] array ) { setParameterList( paramName, array ); } else { - setParameter( paramName, object, determineType( paramName, object.getClass() ) ); + setParameter( paramName, object, + determineType( paramName, object.getClass() ) ); } } } @@ -1203,14 +1371,15 @@ else if ( object instanceof Object[] array ) { } protected Type determineType(String namedParam, Class retType) { - BindableType type = locateBinding( namedParam ).getBindType(); + var type = getQueryParameterBindings().getBinding( namedParam ).getBindType(); if ( type == null ) { type = getParameterMetadata().getQueryParameter( namedParam ).getHibernateType(); } if ( type == null && retType != null ) { type = getSessionFactory().getMappingMetamodel().resolveParameterBindType( retType ); } - if ( retType!= null && !retType.isAssignableFrom( type.getJavaType() ) ) { + if ( retType != null && !retType.isAssignableFrom( type.getJavaType() ) ) { + // TODO: is this really the correct exception type? throw new IllegalStateException( "Parameter not of expected type: " + retType.getName() ); } //noinspection unchecked diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindings.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindings.java index 782eecfc15ff..faffc2c44f27 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindings.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindings.java @@ -58,7 +58,7 @@ default

QueryParameterBinding

getBinding(QueryParameter

parameter) { * * @return The binding, or {@code null} if not yet bound */ -

QueryParameterBinding

getBinding(String name); + QueryParameterBinding getBinding(String name); /** * Access to the binding via position @@ -67,7 +67,7 @@ default

QueryParameterBinding

getBinding(QueryParameter

parameter) { * * @return The binding, or {@code null} if not yet bound */ -

QueryParameterBinding

getBinding(int position); + QueryParameterBinding getBinding(int position); /** * Validate the bindings. Called just before execution @@ -94,15 +94,15 @@ default

QueryParameterBinding

getBinding(QueryParameter

parameter) { * Currently unused and can be safely removed. */ @Deprecated(forRemoval = true, since = "6.6") - @SuppressWarnings({"rawtypes", "unchecked"}) +// @SuppressWarnings({"rawtypes", "unchecked"}) QueryParameterBindings NO_PARAM_BINDINGS = new QueryParameterBindings() { @Override - public boolean isBound(QueryParameterImplementor parameter) { + public boolean isBound(QueryParameterImplementor parameter) { return false; } @Override - public QueryParameterBinding getBinding(QueryParameterImplementor parameter) { + public

QueryParameterBinding

getBinding(QueryParameterImplementor

parameter) { return null; } @@ -117,7 +117,7 @@ public QueryParameterBinding getBinding(int position) { } @Override - public void visitBindings(BiConsumer action) { + public void visitBindings(BiConsumer, ? super QueryParameterBinding> action) { } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedParameter.java index e3b4064d1885..44f8605e4c16 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmNamedParameter.java @@ -75,7 +75,7 @@ public String getName() { @Override public SqmParameter copy() { - return new SqmNamedParameter<>( getName(), allowMultiValuedBinding(), this.getNodeType(), nodeBuilder() ); + return new SqmNamedParameter<>( getName(), allowMultiValuedBinding(), getNodeType(), nodeBuilder() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/EnumJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/EnumJavaType.java index 60241c28889f..8273f2cf908d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/EnumJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/EnumJavaType.java @@ -112,7 +112,12 @@ else if ( Short.class.equals( type ) ) { else if ( Byte.class.equals( type ) ) { return type.cast( toByte( value ) ); } - return type.cast( value ); + else if ( type.isInstance( value )) { + return type.cast( value ); + } + else { + throw unknownUnwrap( type ); + } } @Override @@ -138,9 +143,15 @@ else if ( value instanceof Byte byteValue ) { else if ( value instanceof Number number ) { return fromLong( number.longValue() ); } - else { + else if ( getJavaType().isInstance( value ) ) { + return (T) value; + } + else if ( isInstance( value ) ) { return cast( value ); } + else { + throw unknownWrap( value.getClass() ); + } } /** diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ParameterIsNullTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ParameterIsNullTest.java index 3fdfdc50434e..45cabc140e67 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ParameterIsNullTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ParameterIsNullTest.java @@ -70,7 +70,7 @@ public void testEmptyCollectionParam(SessionFactoryScope scope) { } catch (Exception e) { assertThat( e ).isInstanceOf( IllegalArgumentException.class ); - assertThat( e.getMessage() ).contains( "Parameter value", "did not match expected type" ); + assertThat( e.getMessage() ).contains( "Argument to query parameter has an incompatible type", "is not assignable to" ); } } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/mutation/multitable/IdSelectionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/mutation/multitable/IdSelectionTests.java index 4b21421868fa..288780b1dd95 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/mutation/multitable/IdSelectionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/mutation/multitable/IdSelectionTests.java @@ -12,6 +12,7 @@ import org.hibernate.query.internal.QueryParameterBindingsImpl; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.QueryOptions; +import org.hibernate.query.spi.QueryParameterBinding; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.internal.MatchingIdSelectionHelper; @@ -61,7 +62,8 @@ public void testSecondaryTableRestrictedOnRootTable(SessionFactoryScope scope) { parameterMetadata, scope.getSessionFactory() ); - domainParamBindings.getBinding( "n" ).setBindValue( "abc" ); + ((QueryParameterBinding) domainParamBindings.getBinding( "n" )) + .setBindValue( "abc" ); scope.inTransaction( session -> { @@ -86,7 +88,8 @@ public void testSecondaryTableRestrictedOnNonRootTable(SessionFactoryScope scope parameterMetadata, scope.getSessionFactory() ); - domainParamBindings.getBinding( "d" ).setBindValue( "123" ); + ((QueryParameterBinding) domainParamBindings.getBinding( "d" )) + .setBindValue( "123" ); scope.inTransaction( session -> { @@ -111,7 +114,8 @@ public void testJoinedSubclassRestrictedOnRootTable(SessionFactoryScope scope) { parameterMetadata, scope.getSessionFactory() ); - domainParamBindings.getBinding( "n" ).setBindValue( "Acme" ); + ((QueryParameterBinding) domainParamBindings.getBinding( "n" )) + .setBindValue( "Acme" ); scope.inTransaction( session -> { @@ -136,7 +140,8 @@ public void testJoinedSubclassRestrictedOnNonPrimaryRootTable(SessionFactoryScop parameterMetadata, scope.getSessionFactory() ); - domainParamBindings.getBinding( "n" ).setBindValue( "Acme" ); + ((QueryParameterBinding) domainParamBindings.getBinding( "n" )) + .setBindValue( "Acme" ); scope.inTransaction( session -> { @@ -161,7 +166,8 @@ public void testJoinedSubclassRestrictedOnPrimaryNonRootTable(SessionFactoryScop parameterMetadata, scope.getSessionFactory() ); - domainParamBindings.getBinding( "v" ).setBindValue( "123" ); + ((QueryParameterBinding) domainParamBindings.getBinding( "v" )) + .setBindValue( "123" ); scope.inTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/type/LongListTypeContributorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/LongListTypeContributorTest.java index b158d39cc1d7..8a64c726fcad 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/type/LongListTypeContributorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/LongListTypeContributorTest.java @@ -59,7 +59,7 @@ public void testParameterRegisteredCollection(SessionFactoryScope factoryScope) } ); factoryScope.inTransaction( em -> { - SpecialItem item = (SpecialItem) em.createNativeQuery( + SpecialItem item = em.createNativeQuery( "SELECT * FROM special_table WHERE long_list = :longList", SpecialItem.class ) .setParameter( "longList", longList ) .getSingleResult(); From 0ee5b79c28476e3ad30b54062fea8d4f01b788eb Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 1 Dec 2025 00:08:13 +0100 Subject: [PATCH 059/181] fix a user-written test that asserted some unreasonable things --- .../test/embeddable/EmbeddableWithJavaTypeTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/embeddable/EmbeddableWithJavaTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/embeddable/EmbeddableWithJavaTypeTest.java index b077457e9ea6..a241312e6899 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/embeddable/EmbeddableWithJavaTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/embeddable/EmbeddableWithJavaTypeTest.java @@ -48,10 +48,10 @@ public void injectSessionFactoryScope(SessionFactoryScope scope) { @ValueSource(strings = { "select z from EntityEmbedCustom z where embedCustom.value=:datum", "select z from EntityEmbedCustom z where :datum=embedCustom.value", - "select z from EntityEmbedCustom z where embedCustom=:datum", // this query failed with the bug - "select z from EntityEmbedCustom z where :datum=embedCustom", + "select z from EntityEmbedCustom z where embedCustom.value=:datum", // this query failed with the bug + "select z from EntityEmbedCustom z where :datum=embedCustom.value", "select z from EntityEmbedCustom z where embedCustom.value in (:datum)", - "select z from EntityEmbedCustom z where embedCustom in (:datum)" // failed as well + "select z from EntityEmbedCustom z where embedCustom.value in (:datum)" // failed as well }) void hhh18898Test_embedCustom(String hql) { @@ -81,10 +81,10 @@ void hhh18898Test_embedCustom(String hql) { @ValueSource(strings = { "select z from EntityEmbedNative z where embedNative.value=:datum", "select z from EntityEmbedNative z where :datum=embedNative.value", - "select z from EntityEmbedNative z where embedNative=:datum", // this query failed with the bug - "select z from EntityEmbedNative z where :datum=embedNative", + "select z from EntityEmbedNative z where embedNative.value=:datum", // this query failed with the bug + "select z from EntityEmbedNative z where :datum=embedNative.value", "select z from EntityEmbedNative z where embedNative.value in (:datum)", - "select z from EntityEmbedNative z where embedNative in (:datum)" // failed as well + "select z from EntityEmbedNative z where embedNative.value in (:datum)" // failed as well }) void hhh18898Test_embedSingle(String hql) { From 80acf211ffe80d715a3736bf7dba1e92c80e45df Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 1 Dec 2025 00:36:04 +0100 Subject: [PATCH 060/181] simplify the exception model for query arguments/parameters --- .../query/QueryArgumentException.java | 15 +++- .../query/QueryArgumentTypeException.java | 31 ------- .../spi/AbstractCommonQueryContract.java | 39 ++++----- .../spi/QueryParameterBindingValidator.java | 87 +++++-------------- 4 files changed, 56 insertions(+), 116 deletions(-) delete mode 100644 hibernate-core/src/main/java/org/hibernate/query/QueryArgumentTypeException.java diff --git a/hibernate-core/src/main/java/org/hibernate/query/QueryArgumentException.java b/hibernate-core/src/main/java/org/hibernate/query/QueryArgumentException.java index 828f3ac61e17..1b6705d39364 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/QueryArgumentException.java +++ b/hibernate-core/src/main/java/org/hibernate/query/QueryArgumentException.java @@ -15,11 +15,20 @@ */ public class QueryArgumentException extends IllegalArgumentException { private final Class parameterType; + private final Class argumentType; private final Object argument; public QueryArgumentException(String message, Class parameterType, Object argument) { - super(message); + super( message + " (argument [" + argument + "] is not assignable to " + parameterType.getName() + ")" ); this.parameterType = parameterType; + this.argumentType = argument == null ? null : argument.getClass(); + this.argument = argument; + } + + public QueryArgumentException(String message, Class parameterType, Class argumentType, Object argument) { + super( message + " (" + argumentType.getName() + " is not assignable to " + parameterType.getName() + ")" ); + this.parameterType = parameterType; + this.argumentType = argumentType; this.argument = argument; } @@ -27,6 +36,10 @@ public Class getParameterType() { return parameterType; } + public Class getArgumentType() { + return argumentType; + } + public Object getArgument() { return argument; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/QueryArgumentTypeException.java b/hibernate-core/src/main/java/org/hibernate/query/QueryArgumentTypeException.java deleted file mode 100644 index 150d782cdbd3..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/query/QueryArgumentTypeException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.query; - -/** - * Thrown when an argument bound to a query parameter is of an incompatible type. - * - * @since 7.2 - * - * @author Gavin King - */ -public class QueryArgumentTypeException extends IllegalArgumentException { - private final Class expectedType; - private final Class actualType; - - public QueryArgumentTypeException(String message, Class expectedType, Class actualType) { - super( message + " (" + actualType.getName() + " is not assignable to " + expectedType.getName() + ")" ); - this.expectedType = expectedType; - this.actualType = actualType; - } - - public Class getExpectedType() { - return expectedType; - } - - public Class getActualType() { - return actualType; - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java index d42736c53afd..bd64d63bd8dd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java @@ -35,7 +35,6 @@ import org.hibernate.query.QueryFlushMode; import org.hibernate.query.QueryParameter; import org.hibernate.query.TypedParameterValue; -import org.hibernate.query.QueryArgumentTypeException; import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.internal.QueryOptionsImpl; import org.hibernate.query.sqm.NodeBuilder; @@ -643,10 +642,9 @@ public QueryParameterImplementor getParameter(String name, Class type) try { final var parameter = getParameterMetadata().getQueryParameter( name ); if ( !type.isAssignableFrom( parameter.getParameterType() ) ) { - throw new QueryArgumentTypeException( - "Type specified for parameter named '" + name + "' is incompatible", - parameter.getParameterType(), - type + throw new IllegalArgumentException( + "Type specified for parameter named '" + name + "' is incompatible" + + " (" + parameter.getParameterType().getName() + " is not assignable to " + type.getName() + ")" ); } @SuppressWarnings("unchecked") // safe, just checked @@ -673,10 +671,9 @@ public QueryParameterImplementor getParameter(int position, Class type try { final var parameter = getParameterMetadata().getQueryParameter( position ); if ( !type.isAssignableFrom( parameter.getParameterType() ) ) { - throw new QueryArgumentTypeException( - "Type specified for parameter at position " + position + " is incompatible", - parameter.getParameterType(), - type + throw new IllegalArgumentException( + "Type specified for parameter at position " + position + " is incompatible" + + " (" + parameter.getParameterType().getName() + " is not assignable to " + type.getName() + ")" ); } @SuppressWarnings("unchecked") // safe, just checked @@ -746,9 +743,9 @@ protected

QueryParameterBinding

locateBinding(String name, Class

javaT final var parameterJavaType = parameterType.getJavaType(); if ( !parameterJavaType.isAssignableFrom( javaType ) && !isInstance( parameterType, value ) ) { - throw new QueryArgumentTypeException( + throw new QueryArgumentException( "Argument to parameter named '" + name + "' has an incompatible type", - parameterJavaType, javaType); + parameterJavaType, javaType, value ); } } @SuppressWarnings("unchecked") // safe, just checked @@ -764,9 +761,9 @@ protected

QueryParameterBinding

locateBinding(int position, Class

java final var parameterJavaType = parameterType.getJavaType(); if ( !parameterJavaType.isAssignableFrom( javaType ) && !isInstance( parameterType, value ) ) { - throw new QueryArgumentTypeException( + throw new QueryArgumentException( "Argument to parameter at position " + position + " has an incompatible type", - parameterJavaType, javaType ); + parameterJavaType, javaType, value ); } } @SuppressWarnings("unchecked") // safe, just checked @@ -782,9 +779,9 @@ protected

QueryParameterBinding

locateBinding(String name, Class

javaT final var parameterJavaType = parameterType.getJavaType(); if ( !parameterJavaType.isAssignableFrom( javaType ) && !areInstances( parameterType, values ) ) { - throw new QueryArgumentTypeException( + throw new QueryArgumentException( "Argument to parameter named '" + name + "' has an incompatible type", - parameterJavaType, javaType); + parameterJavaType, javaType, values ); } } @SuppressWarnings("unchecked") // safe, just checked @@ -800,9 +797,9 @@ protected

QueryParameterBinding

locateBinding(int position, Class

java final var parameterJavaType = parameterType.getJavaType(); if ( !parameterJavaType.isAssignableFrom( javaType ) && !areInstances( parameterType, values ) ) { - throw new QueryArgumentTypeException( + throw new QueryArgumentException( "Argument to parameter at position " + position + " has an incompatible type", - parameterJavaType, javaType ); + parameterJavaType, javaType, values ); } } @SuppressWarnings("unchecked") // safe, just checked @@ -865,8 +862,8 @@ private

void setParameterValue(Object value, QueryParameterBinding

bindin final var parameterType = binding.getBindType(); if ( parameterType != null && !isInstanceOrAreInstances( value, binding, parameterType ) ) { - throw new QueryArgumentTypeException( "Argument to query parameter has an incompatible type", - parameterType.getJavaType(), value.getClass() ); + throw new QueryArgumentException( "Argument to query parameter has an incompatible type", + parameterType.getJavaType(), value.getClass(), value ); } @SuppressWarnings("unchecked") // safe, just checked final var castValue = (P) value; @@ -1187,8 +1184,8 @@ private

void setParameterValues(Object[] values, QueryParameterBinding

bi final var parameterType = binding.getBindType(); if ( parameterType != null && !areInstances( values, parameterType ) ) { - throw new QueryArgumentTypeException( "Argument to query parameter has an incompatible type", - parameterType.getJavaType(), values.getClass().getComponentType() ); + throw new QueryArgumentException( "Argument to query parameter has an incompatible type", + parameterType.getJavaType(), values.getClass().getComponentType(), values ); } @SuppressWarnings("unchecked") // safe, just checked final var castArray = (P[]) values; diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindingValidator.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindingValidator.java index 83d1d482e522..ee4b10cf97bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindingValidator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindingValidator.java @@ -9,7 +9,6 @@ import java.util.Date; import org.hibernate.query.QueryArgumentException; -import org.hibernate.query.sqm.SqmBindableType; import org.hibernate.type.BindableType; import org.hibernate.type.BindingContext; import org.hibernate.type.descriptor.java.JavaType; @@ -40,8 +39,8 @@ public void validate( return; } - final SqmBindableType sqmExpressible = bindingContext.resolveExpressible( paramType ); - final Class parameterJavaType = + final var sqmExpressible = bindingContext.resolveExpressible( paramType ); + final var parameterJavaType = paramType.getJavaType() != null ? paramType.getJavaType() : sqmExpressible.getJavaType(); @@ -50,9 +49,9 @@ public void validate( if ( bind instanceof Collection collection && !Collection.class.isAssignableFrom( parameterJavaType ) ) { // we have a collection passed in where we are expecting a non-collection. - // NOTE : this can happen in Hibernate's notion of "parameter list" binding - // NOTE2 : the case of a collection value and an expected collection (if that can even happen) - // will fall through to the main check. + // NOTE: this can happen in Hibernate's notion of "parameter list" binding + // NOTE2: the case of a collection value and an expected collection (if that can even happen) + // will fall through to the main check. validateCollectionValuedParameterBinding( parameterJavaType, collection, @@ -72,17 +71,8 @@ else if ( !isValidBindValue( bind, temporalPrecision ) ) { - throw new QueryArgumentException( - String.format( - "Argument [%s] of type [%s] did not match parameter type [%s (%s)]", - bind, - bind.getClass().getName(), - parameterJavaType.getName(), - extractName( temporalPrecision ) - ), - parameterJavaType, - bind - ); + throw new QueryArgumentException( "Argument did not match parameter type", + parameterJavaType, bind ); } } // else nothing we can check @@ -99,17 +89,8 @@ private void validateCollectionValuedParameterBinding( // validate the elements... for ( Object element : value ) { if ( !isValidBindValue( parameterType, element, temporalType ) ) { - throw new QueryArgumentException( - String.format( - "Parameter value element [%s] did not match expected type [%s (%s)]", - element, - parameterType.getName(), - extractName( temporalType ) - ) - , - parameterType, - element - ); + throw new QueryArgumentException( "Argument element did not match parameter type", + parameterType, element ); } } } @@ -175,11 +156,12 @@ else if ( expectedType.isInstance( value ) ) { return true; } else if ( temporalType != null ) { - final boolean parameterDeclarationIsTemporal = Date.class.isAssignableFrom( expectedType ) + final boolean parameterDeclarationIsTemporal = + Date.class.isAssignableFrom( expectedType ) || Calendar.class.isAssignableFrom( expectedType ); - final boolean bindIsTemporal = value instanceof Date + final boolean bindIsTemporal = + value instanceof Date || value instanceof Calendar; - return parameterDeclarationIsTemporal && bindIsTemporal; } @@ -191,31 +173,18 @@ private void validateArrayValuedParameterBinding( Object value, TemporalType temporalType) { if ( !parameterType.isArray() ) { - throw new QueryArgumentException( - String.format( - "Encountered array-valued parameter binding, but was expecting [%s (%s)]", - parameterType.getName(), - extractName( temporalType ) - ), - parameterType, - value - ); + throw new QueryArgumentException( "Unexpected array-valued parameter binding", + parameterType, value ); } - if ( value.getClass().getComponentType().isPrimitive() ) { + final var componentType = value.getClass().getComponentType(); + final var parameterComponentType = parameterType.getComponentType(); + if ( componentType.isPrimitive() ) { // we have a primitive array. we validate that the actual array has the component type (type of elements) // we expect based on the component type of the parameter specification - if ( !parameterType.getComponentType().isAssignableFrom( value.getClass().getComponentType() ) ) { - throw new QueryArgumentException( - String.format( - "Primitive array-valued parameter bind value type [%s] did not match expected type [%s (%s)]", - value.getClass().getComponentType().getName(), - parameterType.getName(), - extractName( temporalType ) - ), - parameterType, - value - ); + if ( !parameterComponentType.isAssignableFrom( componentType ) ) { + throw new QueryArgumentException( "Primitive array-valued argument type did not match parameter type", + parameterType, value ); } } else { @@ -223,17 +192,9 @@ private void validateArrayValuedParameterBinding( // the type we expect based on the component type of the parameter specification final Object[] array = (Object[]) value; for ( Object element : array ) { - if ( !isValidBindValue( parameterType.getComponentType(), element, temporalType ) ) { - throw new QueryArgumentException( - String.format( - "Array-valued parameter value element [%s] did not match expected type [%s (%s)]", - element, - parameterType.getName(), - extractName( temporalType ) - ), - parameterType, - array - ); + if ( !isValidBindValue( parameterComponentType, element, temporalType ) ) { + throw new QueryArgumentException( "Array-valued argument type did not match parameter type", + parameterType, array ); } } } From 043ee483bab0e091aeff5e5f81910fd0ef162145 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 1 Dec 2025 00:43:13 +0100 Subject: [PATCH 061/181] very minor code refactoring --- .../spi/AbstractCommonQueryContract.java | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java index bd64d63bd8dd..84e1182c95b9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java @@ -641,10 +641,11 @@ public QueryParameterImplementor getParameter(String name, Class type) checkOpenNoRollback(); try { final var parameter = getParameterMetadata().getQueryParameter( name ); - if ( !type.isAssignableFrom( parameter.getParameterType() ) ) { + final var parameterType = parameter.getParameterType(); + if ( !type.isAssignableFrom( parameterType ) ) { throw new IllegalArgumentException( "Type specified for parameter named '" + name + "' is incompatible" - + " (" + parameter.getParameterType().getName() + " is not assignable to " + type.getName() + ")" + + " (" + parameterType.getName() + " is not assignable to " + type.getName() + ")" ); } @SuppressWarnings("unchecked") // safe, just checked @@ -670,10 +671,11 @@ public QueryParameterImplementor getParameter(int position, Class type checkOpenNoRollback(); try { final var parameter = getParameterMetadata().getQueryParameter( position ); - if ( !type.isAssignableFrom( parameter.getParameterType() ) ) { + final var parameterType = parameter.getParameterType(); + if ( !type.isAssignableFrom( parameterType ) ) { throw new IllegalArgumentException( "Type specified for parameter at position " + position + " is incompatible" - + " (" + parameter.getParameterType().getName() + " is not assignable to " + type.getName() + ")" + + " (" + parameterType.getName() + " is not assignable to " + type.getName() + ")" ); } @SuppressWarnings("unchecked") // safe, just checked @@ -702,11 +704,16 @@ protected

QueryParameterBinding

locateBinding(Parameter

parameter, P v if ( parameter instanceof QueryParameterImplementor

parameterImplementor ) { return locateBinding( parameterImplementor ); } - else if ( parameter.getName() != null ) { - return locateBinding( parameter.getName(), parameter.getParameterType(), value ); - } - else if ( parameter.getPosition() != null ) { - return locateBinding( parameter.getPosition(), parameter.getParameterType(), value ); + else { + final String name = parameter.getName(); + final Integer position = parameter.getPosition(); + final var parameterType = parameter.getParameterType(); + if ( name != null ) { + return locateBinding( name, parameterType, value ); + } + else if ( position != null ) { + return locateBinding( position, parameterType, value ); + } } throw getExceptionConverter().convert( @@ -718,11 +725,16 @@ protected

QueryParameterBinding

locateBinding(Parameter

parameter, Col if ( parameter instanceof QueryParameterImplementor

parameterImplementor ) { return locateBinding( parameterImplementor ); } - else if ( parameter.getName() != null ) { - return locateBinding( parameter.getName(), parameter.getParameterType(), values ); - } - else if ( parameter.getPosition() != null ) { - return locateBinding( parameter.getPosition(), parameter.getParameterType(), values ); + else { + final String name = parameter.getName(); + final Integer position = parameter.getPosition(); + final var parameterType = parameter.getParameterType(); + if ( name != null ) { + return locateBinding( name, parameterType, values ); + } + else if ( position != null ) { + return locateBinding( position, parameterType, values ); + } } throw getExceptionConverter().convert( @@ -732,7 +744,8 @@ else if ( parameter.getPosition() != null ) { protected

QueryParameterBinding

locateBinding(QueryParameterImplementor

parameter) { getCheckOpen(); - return getQueryParameterBindings().getBinding( getQueryParameter( parameter ) ); + return getQueryParameterBindings() + .getBinding( getQueryParameter( parameter ) ); } protected

QueryParameterBinding

locateBinding(String name, Class

javaType, P value) { From 1c548c05b9d109e7c5204cc274f5e3f7bf15f5a5 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 1 Dec 2025 10:19:46 +0100 Subject: [PATCH 062/181] introduce QueryArguments allow sensible conversions on query parameter arguments as defined by what our JavaTypes already know how to convert --- .../query/internal/QueryArguments.java | 99 +++++++++ .../internal/QueryParameterBindingImpl.java | 32 +-- .../internal/QueryParameterBindingsImpl.java | 12 +- .../spi/AbstractCommonQueryContract.java | 86 +------- .../spi/QueryParameterBindingValidator.java | 208 +++++------------- .../type/descriptor/java/BooleanJavaType.java | 32 ++- .../descriptor/java/CalendarDateJavaType.java | 15 ++ .../descriptor/java/CalendarJavaType.java | 17 +- .../query/QueryParametersValidationTest.java | 2 +- .../java/BooleanJavaTypeDescriptorTest.java | 3 +- .../DetachedProxyAsQueryParameterTest.java | 21 +- .../hql/QueryParametersValidationTest.java | 13 ++ 12 files changed, 270 insertions(+), 270 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/query/internal/QueryArguments.java diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryArguments.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryArguments.java new file mode 100644 index 000000000000..872dbfe53959 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryArguments.java @@ -0,0 +1,99 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.query.internal; + +import jakarta.persistence.metamodel.Type; +import org.hibernate.HibernateException; +import org.hibernate.type.BindingContext; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.java.spi.EntityJavaType; + +import java.util.Collection; + +import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; + +/** + * @since 7.3 + * + * @author Gavin King + */ +public class QueryArguments { + + private static boolean isInstance(Object value, JavaType javaType) { + try { + // special handling for entity arguments due to + // the possibility of an uninitialized proxy + // (which we don't want or need to fetch) + if ( javaType instanceof EntityJavaType ) { + final var javaTypeClass = javaType.getJavaTypeClass(); + final var initializer = extractLazyInitializer( value ); + final var valueEntityClass = + initializer != null + ? initializer.getPersistentClass() + : value.getClass(); + // accept assignability in either direction + return javaTypeClass.isAssignableFrom( valueEntityClass ) + || valueEntityClass.isAssignableFrom( javaTypeClass ); + } + else { + // require that the argument be assignable to the parameter + return javaType.isInstance( javaType.coerce( value ) ); + } + } + catch (HibernateException ce) { + return false; + } + } + + public static boolean isInstance( + Type parameterType, Object value, + BindingContext bindingContext) { + if ( value == null ) { + return true; + } + final var sqmExpressible = bindingContext.resolveExpressible( parameterType ); + assert sqmExpressible != null; + final var javaType = sqmExpressible.getExpressibleJavaType(); + return isInstance( value, javaType ); + } + + public static boolean areInstances( + Type parameterType, Collection values, + BindingContext bindingContext) { + if ( values.isEmpty() ) { + return true; + } + final var sqmExpressible = bindingContext.resolveExpressible( parameterType ); + assert sqmExpressible != null; + final var javaType = sqmExpressible.getExpressibleJavaType(); + for ( Object value : values ) { + if ( !isInstance( value, javaType ) ) { + return false; + } + } + return true; + } + + public static boolean areInstances( + Type parameterType, Object[] values, + BindingContext bindingContext) { + if ( values.length == 0 ) { + return true; + } + if ( parameterType.getJavaType() + .isAssignableFrom( values.getClass().getComponentType() ) ) { + return true; + } + final var sqmExpressible = bindingContext.resolveExpressible( parameterType ); + assert sqmExpressible != null; + final var javaType = sqmExpressible.getExpressibleJavaType(); + for ( Object value : values ) { + if ( !isInstance( value, javaType ) ) { + return false; + } + } + return true; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java index 4c9bdd7d91b5..0acc3415c676 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java @@ -16,13 +16,13 @@ import org.hibernate.query.QueryParameter; import org.hibernate.query.spi.QueryParameterBinding; import org.hibernate.query.spi.QueryParameterBindingTypeResolver; -import org.hibernate.query.spi.QueryParameterBindingValidator; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.spi.TypeConfiguration; import jakarta.persistence.TemporalType; +import static org.hibernate.query.spi.QueryParameterBindingValidator.validate; import static org.hibernate.type.descriptor.java.JavaTypeHelper.isTemporal; import static org.hibernate.type.internal.BindingTypeHelper.resolveTemporalPrecision; @@ -116,7 +116,7 @@ public T getBindValue() { public void setBindValue(T value, boolean resolveJdbcTypeIfNecessary) { if ( !handleAsMultiValue( value, null ) ) { final Object coerced = coerceIfNotJpa( value ); - validate( coerced ); + validate( getBindType(), coerced, sessionFactory ); if ( value == null ) { // needed when setting a null value to the parameter of a native SQL query @@ -174,7 +174,7 @@ public void setBindValue(T value, @Nullable BindableType clarifiedType) { } final Object coerced = coerce( value ); - validate( coerced, clarifiedType ); + validate( clarifiedType, coerced, sessionFactory ); bindValue( coerced ); } } @@ -187,7 +187,7 @@ public void setBindValue(T value, TemporalType temporalTypePrecision) { } final Object coerced = coerceIfNotJpa( value ); - validate( coerced, temporalTypePrecision ); + validate( getBindType(), coerced, sessionFactory ); bindValue( coerced ); setExplicitTemporalPrecision( temporalTypePrecision ); } @@ -218,15 +218,19 @@ public void setBindValues(Collection values) { this.bindValue = null; this.bindValues = values; + final T value = firstNonNull( values ); + if ( canBindValueBeSet( value, bindType ) ) { + bindType = getParameterBindingTypeResolver().resolveParameterBindType( value ); + } + } + + private static @Nullable T firstNonNull(Collection values) { final var iterator = values.iterator(); T value = null; while ( value == null && iterator.hasNext() ) { value = iterator.next(); } - - if ( canBindValueBeSet( value, bindType ) ) { - bindType = getParameterBindingTypeResolver().resolveParameterBindType( value ); - } + return value; } @Override @@ -284,18 +288,6 @@ else if ( type instanceof BasicValuedMapping basicValuedMapping ) { return false; } - private void validate(Object value) { - QueryParameterBindingValidator.INSTANCE.validate( getBindType(), value, getCriteriaBuilder() ); - } - - private void validate(Object value, BindableType clarifiedType) { - QueryParameterBindingValidator.INSTANCE.validate( clarifiedType, value, getCriteriaBuilder() ); - } - - private void validate(Object value, TemporalType clarifiedTemporalType) { - QueryParameterBindingValidator.INSTANCE.validate( getBindType(), value, clarifiedTemporalType, getCriteriaBuilder() ); - } - public TypeConfiguration getTypeConfiguration() { return sessionFactory.getTypeConfiguration(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java index 2561fd7abd5e..a7f63b73fa8e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java @@ -68,7 +68,8 @@ private QueryParameterBindingsImpl( this.parameterBindingMap = linkedMapOfSize( queryParameters.size() ); this.parameterBindingMapByNameOrPosition = mapOfSize( queryParameters.size() ); for ( var queryParameter : queryParameters ) { - parameterBindingMap.put( queryParameter, createBinding( sessionFactory, parameterMetadata, queryParameter ) ); + parameterBindingMap.put( queryParameter, + createBinding( sessionFactory, parameterMetadata, queryParameter ) ); } for ( var entry : parameterBindingMap.entrySet() ) { final var queryParameter = entry.getKey(); @@ -129,9 +130,12 @@ public

QueryParameterBinding

getBinding(QueryParameterImplementor

para "Cannot create binding for parameter reference [" + parameter + "] - reference is not a parameter of this query" ); } - //TODO: typecheck! - //noinspection unchecked - return (QueryParameterBinding

) binding; + if ( !binding.getQueryParameter().equals( parameter ) ) { + throw new IllegalStateException("Parameter binding corrupted for: " + parameter.getName() ); + } + @SuppressWarnings("unchecked") // safe because we checked the parameter + final var castBinding = (QueryParameterBinding

) binding; + return castBinding; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java index 84e1182c95b9..4817faa4374a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java @@ -89,6 +89,8 @@ import static org.hibernate.jpa.internal.util.ConfigurationHelper.getCacheMode; import static org.hibernate.jpa.internal.util.ConfigurationHelper.getInteger; import static org.hibernate.query.QueryLogging.QUERY_MESSAGE_LOGGER; +import static org.hibernate.query.internal.QueryArguments.areInstances; +import static org.hibernate.query.internal.QueryArguments.isInstance; /** * Base implementation of {@link CommonQueryContract}. @@ -755,7 +757,7 @@ protected

QueryParameterBinding

locateBinding(String name, Class

javaT if ( parameterType != null ) { final var parameterJavaType = parameterType.getJavaType(); if ( !parameterJavaType.isAssignableFrom( javaType ) - && !isInstance( parameterType, value ) ) { + && !isInstance( parameterType, value, getNodeBuilder() ) ) { throw new QueryArgumentException( "Argument to parameter named '" + name + "' has an incompatible type", parameterJavaType, javaType, value ); @@ -773,7 +775,7 @@ protected

QueryParameterBinding

locateBinding(int position, Class

java if ( parameterType != null ) { final var parameterJavaType = parameterType.getJavaType(); if ( !parameterJavaType.isAssignableFrom( javaType ) - && !isInstance( parameterType, value ) ) { + && !isInstance( parameterType, value, getNodeBuilder() ) ) { throw new QueryArgumentException( "Argument to parameter at position " + position + " has an incompatible type", parameterJavaType, javaType, value ); @@ -791,7 +793,7 @@ protected

QueryParameterBinding

locateBinding(String name, Class

javaT if ( parameterType != null ) { final var parameterJavaType = parameterType.getJavaType(); if ( !parameterJavaType.isAssignableFrom( javaType ) - && !areInstances( parameterType, values ) ) { + && !areInstances( parameterType, values, getNodeBuilder() ) ) { throw new QueryArgumentException( "Argument to parameter named '" + name + "' has an incompatible type", parameterJavaType, javaType, values ); @@ -809,7 +811,7 @@ protected

QueryParameterBinding

locateBinding(int position, Class

java if ( parameterType != null ) { final var parameterJavaType = parameterType.getJavaType(); if ( !parameterJavaType.isAssignableFrom( javaType ) - && !areInstances( parameterType, values ) ) { + && !areInstances( parameterType, values, getNodeBuilder() ) ) { throw new QueryArgumentException( "Argument to parameter at position " + position + " has an incompatible type", parameterJavaType, javaType, values ); @@ -886,8 +888,8 @@ private

void setParameterValue(Object value, QueryParameterBinding

bindin private

boolean isInstanceOrAreInstances( Object value, QueryParameterBinding

binding, BindableType parameterType) { return binding.isMultiValued() && value instanceof Collection values - ? areInstances( parameterType, values ) - : isInstance( parameterType, value ); + ? areInstances( parameterType, values, getNodeBuilder() ) + : isInstance( parameterType, value, getNodeBuilder() ); } @Override @@ -916,8 +918,8 @@ private boolean multipleBinding(QueryParameter parameter, Object value){ final var hibernateType = parameter.getHibernateType(); return hibernateType == null || values.isEmpty() - || !isInstance( hibernateType, value ) - || isInstance( hibernateType, values.iterator().next() ); + || !isInstance( hibernateType, value, getNodeBuilder() ) + || isInstance( hibernateType, values.iterator().next(), getNodeBuilder() ); } else { return false; @@ -932,67 +934,6 @@ private void setTypedParameter(int position, TypedParameterValue typedVal setParameter( position, typedValue.value(), typedValue.type() ); } - private boolean isInstance(Type parameterType, Object value) { - if ( value == null ) { - return true; - } - final var sqmExpressible = getNodeBuilder().resolveExpressible( parameterType ); - assert sqmExpressible != null; - final var javaType = sqmExpressible.getExpressibleJavaType(); - if ( !javaType.isInstance( value ) ) { - try { - // if this succeeds, we are good - javaType.wrap( value, session ); - } - catch ( HibernateException|UnsupportedOperationException e) { - return false; - } - } - return true; - } - - private boolean areInstances(Type parameterType, Collection values) { - if ( values.isEmpty() ) { - return true; - } - final var sqmExpressible = getNodeBuilder().resolveExpressible( parameterType ); - assert sqmExpressible != null; - final var javaType = sqmExpressible.getExpressibleJavaType(); - for ( Object value : values ) { - if ( !javaType.isInstance( value ) ) { - try { - // if this succeeds, we are good - javaType.wrap( value, session ); - } - catch (HibernateException | UnsupportedOperationException e) { - return false; - } - } - } - return true; - } - - private boolean areInstances(Type parameterType, Object[] values) { - if ( values.length == 0 ) { - return true; - } - final var sqmExpressible = getNodeBuilder().resolveExpressible( parameterType ); - assert sqmExpressible != null; - final var javaType = sqmExpressible.getExpressibleJavaType(); - for ( Object value : values ) { - if ( !javaType.isInstance( value ) ) { - try { - // if this succeeds, we are good - javaType.wrap( value, session ); - } - catch (HibernateException | UnsupportedOperationException e) { - return false; - } - } - } - return true; - } - private NodeBuilder getNodeBuilder() { return getSessionFactory().getQueryEngine().getCriteriaBuilder(); } @@ -1196,7 +1137,7 @@ public CommonQueryContract setParameterList(String name, Object[] values) { private

void setParameterValues(Object[] values, QueryParameterBinding

binding) { final var parameterType = binding.getBindType(); if ( parameterType != null - && !areInstances( values, parameterType ) ) { + && !areInstances( parameterType, values, getNodeBuilder() ) ) { throw new QueryArgumentException( "Argument to query parameter has an incompatible type", parameterType.getJavaType(), values.getClass().getComponentType(), values ); } @@ -1205,11 +1146,6 @@ private

void setParameterValues(Object[] values, QueryParameterBinding

bi binding.setBindValues( List.of( castArray ) ); } - private

boolean areInstances(Object[] values, BindableType parameterType) { - return parameterType.getJavaType().isAssignableFrom( values.getClass().getComponentType() ) - || areInstances( parameterType, values ); - } - @Override public

CommonQueryContract setParameterList(String name, P[] values, Class

javaType) { final var javaDescriptor = getJavaType( javaType ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindingValidator.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindingValidator.java index ee4b10cf97bd..351f874e8711 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindingValidator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindingValidator.java @@ -4,174 +4,81 @@ */ package org.hibernate.query.spi; -import java.util.Calendar; import java.util.Collection; -import java.util.Date; +import org.hibernate.Internal; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.QueryArgumentException; import org.hibernate.type.BindableType; -import org.hibernate.type.BindingContext; -import org.hibernate.type.descriptor.java.JavaType; -import jakarta.persistence.TemporalType; +import static org.hibernate.query.internal.QueryArguments.areInstances; +import static org.hibernate.query.internal.QueryArguments.isInstance; /** * @author Andrea Boriero */ +@Internal public class QueryParameterBindingValidator { - public static final QueryParameterBindingValidator INSTANCE = new QueryParameterBindingValidator(); - private QueryParameterBindingValidator() { } - public void validate(BindableType paramType, Object bind, BindingContext bindingContext) { - validate( paramType, bind, null, bindingContext ); - } - - public void validate( - BindableType paramType, - Object bind, - TemporalType temporalPrecision, - BindingContext bindingContext) { - if ( bind == null || paramType == null ) { - // nothing we can check - return; - } - - final var sqmExpressible = bindingContext.resolveExpressible( paramType ); - final var parameterJavaType = - paramType.getJavaType() != null - ? paramType.getJavaType() - : sqmExpressible.getJavaType(); - - if ( parameterJavaType != null ) { - if ( bind instanceof Collection collection - && !Collection.class.isAssignableFrom( parameterJavaType ) ) { - // we have a collection passed in where we are expecting a non-collection. - // NOTE: this can happen in Hibernate's notion of "parameter list" binding - // NOTE2: the case of a collection value and an expected collection (if that can even happen) - // will fall through to the main check. - validateCollectionValuedParameterBinding( - parameterJavaType, - collection, - temporalPrecision - ); - } - else if ( bind.getClass().isArray() ) { - validateArrayValuedParameterBinding( - parameterJavaType, - bind, - temporalPrecision - ); - } - else if ( !isValidBindValue( - sqmExpressible.getExpressibleJavaType(), - parameterJavaType, - bind, - temporalPrecision - ) ) { - throw new QueryArgumentException( "Argument did not match parameter type", - parameterJavaType, bind ); + public static void validate(BindableType parameterType, Object argument, SessionFactoryImplementor factory) { + if ( argument != null && parameterType != null ) { + final var parameterJavaType = getParameterJavaType( parameterType, factory ); + if ( parameterJavaType != null ) { + if ( argument instanceof Collection collection + && !Collection.class.isAssignableFrom( parameterJavaType ) ) { + // we have a collection passed in where we are expecting a non-collection. + // NOTE: this can happen in Hibernate's notion of "parameter list" binding + // NOTE2: the case of a collection value and an expected collection (if that can even happen) + // will fall through to the main check. + validateCollectionValuedParameterBinding( parameterType, parameterJavaType, collection, factory ); + } + else if ( argument.getClass().isArray() ) { + validateArrayValuedParameterBinding( parameterJavaType, argument ); + } + else { + validateSingleValuedParameterBinding( parameterType, parameterJavaType, argument, factory ); + } } + // else nothing we can check } - // else nothing we can check - } - - private String extractName(TemporalType temporalType) { - return temporalType == null ? "n/a" : temporalType.name(); } - private void validateCollectionValuedParameterBinding( - Class parameterType, - Collection value, - TemporalType temporalType) { - // validate the elements... - for ( Object element : value ) { - if ( !isValidBindValue( parameterType, element, temporalType ) ) { - throw new QueryArgumentException( "Argument element did not match parameter type", - parameterType, element ); - } - } + private static Class getParameterJavaType(BindableType parameterType, SessionFactoryImplementor factory) { + final var javaType = parameterType.getJavaType(); + return javaType != null + ? javaType + : factory.getQueryEngine().getCriteriaBuilder() + .resolveExpressible( parameterType ) + .getJavaType(); } - private static boolean isValidBindValue( - JavaType expectedJavaType, - Class expectedType, + private static void validateSingleValuedParameterBinding( + BindableType parameterType, Class parameterJavaType, Object value, - TemporalType temporalType) { - if ( value == null ) { - return true; - } - else if ( expectedJavaType.isInstance( value ) ) { - return true; - } - else if ( temporalType != null ) { - final boolean parameterDeclarationIsTemporal = Date.class.isAssignableFrom( expectedType ) - || Calendar.class.isAssignableFrom( expectedType ); - final boolean bindIsTemporal = value instanceof Date - || value instanceof Calendar; - - return parameterDeclarationIsTemporal && bindIsTemporal; + SessionFactoryImplementor factory) { + if ( !isInstance( parameterType, value, + factory.getQueryEngine().getCriteriaBuilder() ) ) { + throw new QueryArgumentException( "Argument did not match parameter type", + parameterJavaType, value ); } - - return false; } - private static boolean isValidBindValue( - Class expectedType, - Object value, - TemporalType temporalType) { - if ( expectedType.isPrimitive() ) { - if ( expectedType == boolean.class ) { - return value instanceof Boolean; - } - else if ( expectedType == char.class ) { - return value instanceof Character; - } - else if ( expectedType == byte.class ) { - return value instanceof Byte; - } - else if ( expectedType == short.class ) { - return value instanceof Short; - } - else if ( expectedType == int.class ) { - return value instanceof Integer; - } - else if ( expectedType == long.class ) { - return value instanceof Long; - } - else if ( expectedType == float.class ) { - return value instanceof Float; - } - else if ( expectedType == double.class ) { - return value instanceof Double; - } - return false; - } - else if ( value == null) { - return true; - } - else if ( expectedType.isInstance( value ) ) { - return true; - } - else if ( temporalType != null ) { - final boolean parameterDeclarationIsTemporal = - Date.class.isAssignableFrom( expectedType ) - || Calendar.class.isAssignableFrom( expectedType ); - final boolean bindIsTemporal = - value instanceof Date - || value instanceof Calendar; - return parameterDeclarationIsTemporal && bindIsTemporal; - } + private static void validateCollectionValuedParameterBinding( + BindableType parameterType, Class parameterJavaType, + Collection values, + SessionFactoryImplementor factory) { + if ( !areInstances( parameterType, values, + factory.getQueryEngine().getCriteriaBuilder() ) ) { + throw new QueryArgumentException( "Collection-valued argument did not match parameter type", + parameterJavaType, values ); - return false; + } } - private void validateArrayValuedParameterBinding( - Class parameterType, - Object value, - TemporalType temporalType) { + private static void validateArrayValuedParameterBinding(Class parameterType, Object value) { if ( !parameterType.isArray() ) { throw new QueryArgumentException( "Unexpected array-valued parameter binding", parameterType, value ); @@ -180,21 +87,22 @@ private void validateArrayValuedParameterBinding( final var componentType = value.getClass().getComponentType(); final var parameterComponentType = parameterType.getComponentType(); if ( componentType.isPrimitive() ) { - // we have a primitive array. we validate that the actual array has the component type (type of elements) - // we expect based on the component type of the parameter specification + // We have a primitive array. + // We validate that the actual array has the component type (type of elements) + // that we expect based on the component type of the parameter specification. if ( !parameterComponentType.isAssignableFrom( componentType ) ) { throw new QueryArgumentException( "Primitive array-valued argument type did not match parameter type", parameterType, value ); } } else { - // we have an object array. Here we loop over the array and physically check each element against - // the type we expect based on the component type of the parameter specification - final Object[] array = (Object[]) value; - for ( Object element : array ) { - if ( !isValidBindValue( parameterComponentType, element, temporalType ) ) { - throw new QueryArgumentException( "Array-valued argument type did not match parameter type", - parameterType, array ); + // We have an object array. + // Here we loop over the array and physically check each element against the + // type we expect based on the component type of the parameter specification. + for ( Object element : (Object[]) value ) { + if ( element != null && !parameterComponentType.isInstance( element ) ) { + throw new QueryArgumentException( "Array element did not match parameter element type", + parameterComponentType, element ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java index 3bc1e90b605b..41553b340ab9 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java @@ -29,6 +29,7 @@ public class BooleanJavaType extends AbstractClassJavaType implements private final char characterValueFalse; private final char characterValueTrueLC; + private final char characterValueFalseLC; private final String stringValueTrue; private final String stringValueFalse; @@ -43,6 +44,7 @@ public BooleanJavaType(char characterValueTrue, char characterValueFalse) { this.characterValueFalse = toUpperCase( characterValueFalse ); characterValueTrueLC = Character.toLowerCase( characterValueTrue ); + characterValueFalseLC = Character.toLowerCase( characterValueFalse ); stringValueTrue = String.valueOf( characterValueTrue ); stringValueFalse = String.valueOf( characterValueFalse ); @@ -123,16 +125,35 @@ public Boolean wrap(X value, WrapperOptions options) { return number.intValue() != 0; } if (value instanceof Character character) { - return isTrue( character ); + if ( isTrue( character ) ) { + return true; + } + if ( isFalse( character ) ) { + return false; + } + throw new IllegalArgumentException( "Cannot convert Character value '" + character + "' to Boolean" ); } if (value instanceof String string) { - return isTrue( string ); + if ( isTrue( string ) ) { + return true; + } + if ( isFalse( string ) ) { + return false; + } + throw new IllegalArgumentException( "Cannot convert value '" + string + "' to Boolean" ); } throw unknownWrap( value.getClass() ); } private boolean isTrue(String strValue) { - return strValue != null && !strValue.isEmpty() && isTrue( strValue.charAt(0) ); + return strValue != null + && !strValue.isEmpty() + && isTrue( strValue.charAt(0) ); + } + + private boolean isFalse(String strValue) { + return strValue != null + && ( strValue.isEmpty() || isFalse( strValue.charAt(0) ) ); } private boolean isTrue(char charValue) { @@ -140,6 +161,11 @@ private boolean isTrue(char charValue) { || charValue == characterValueTrueLC; } + private boolean isFalse(char charValue) { + return charValue == characterValueFalse + || charValue == characterValueFalseLC; + } + public int toInt(Boolean value) { return value ? 1 : 0; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java index c458b8b0c7c5..336041f99ba0 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java @@ -88,6 +88,21 @@ public int extractHashCode(Calendar value) { return hashCode; } + @Override + public boolean isInstance(Object value) { + return value instanceof Calendar; + } + + @Override + public Calendar cast(Object value) { + return (Calendar) value; + } + + @Override + public Calendar coerce(Object value) { + return wrap( value, null ); + } + public X unwrap(Calendar value, Class type, WrapperOptions options) { if ( value == null ) { return null; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java index 10c49e839895..324b4d3d919c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java @@ -105,6 +105,21 @@ public int extractHashCode(Calendar value) { return hashCode; } + @Override + public boolean isInstance(Object value) { + return value instanceof Calendar; + } + + @Override + public Calendar cast(Object value) { + return (Calendar) value; + } + + @Override + public Calendar coerce(Object value) { + return wrap( value, null ); + } + public X unwrap(Calendar value, Class type, WrapperOptions options) { if ( value == null ) { return null; @@ -135,7 +150,7 @@ else if (value instanceof Calendar calendar) { return calendar; } else if ( value instanceof java.util.Date date ) { - final Calendar cal = new GregorianCalendar(); + final var cal = new GregorianCalendar(); cal.setTime( date ); return cal; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryParametersValidationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryParametersValidationTest.java index 80932e0088fd..fb82b2599cf8 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryParametersValidationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/query/QueryParametersValidationTest.java @@ -43,7 +43,7 @@ public void setParameterWithWrongTypeShouldThrowIllegalArgumentException(EntityM scope.inEntityManager( entityManager -> { Assertions.assertThrows( IllegalArgumentException.class, - () -> entityManager.createQuery( "select e from TestEntity e where e.id = :id" ).setParameter( "id", 1 ) + () -> entityManager.createQuery( "select e from TestEntity e where e.id = :id" ).setParameter( "id", "X" ) ); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/BooleanJavaTypeDescriptorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/BooleanJavaTypeDescriptorTest.java index 29ee5e6ad8c3..213e8cd965bf 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/BooleanJavaTypeDescriptorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/type/java/BooleanJavaTypeDescriptorTest.java @@ -7,6 +7,7 @@ import jakarta.persistence.AttributeConverter; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.dialect.Dialect; +import org.hibernate.testing.orm.junit.FailureExpected; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.type.NumericBooleanConverter; import org.hibernate.type.TrueFalseConverter; @@ -92,7 +93,7 @@ public void testWrapShouldReturnFalseWhenFStringGiven() { assertFalse(result); } - @Test + @Test @FailureExpected // this was historically allowed public void testWrapShouldReturnFalseWhenRandomStringGiven() { // given // when diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/DetachedProxyAsQueryParameterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/DetachedProxyAsQueryParameterTest.java index 09b6687c0735..9770accf0c9c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/DetachedProxyAsQueryParameterTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/DetachedProxyAsQueryParameterTest.java @@ -5,7 +5,6 @@ package org.hibernate.orm.test.proxy; import org.hibernate.Hibernate; -import org.hibernate.LazyInitializationException; import org.hibernate.query.Query; import org.hibernate.testing.orm.junit.DomainModel; @@ -23,7 +22,6 @@ import jakarta.persistence.ManyToOne; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; @DomainModel( annotatedClasses = { @@ -71,24 +69,17 @@ public void testQuery(SessionFactoryScope scope) { scope.inSession( session -> { session.createQuery( - "select d from Department d where d = :department" ) + "select d from Department d where d = :department" ) .setParameter( "department", employee.getDepartment() ).list(); } ); - assertThrows( - LazyInitializationException.class, () -> { - scope.inSession( - session -> { - // In order to validate the parameter and check that it is an instance of SpecialDepartment - // the Department proxy have to be initialized but being a detached instance - // a LazyInitializationException is thrown - session.createQuery( - "select d from SpecialDepartment d where d = :department" - ).setParameter( "department", employee.getDepartment() ).list(); - } - ); + scope.inSession( + session -> { + session.createQuery( + "select d from SpecialDepartment d where d = :department" ) + .setParameter( "department", employee.getDepartment() ).list(); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/QueryParametersValidationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/QueryParametersValidationTest.java index c85e0617afdf..2845927f0922 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/QueryParametersValidationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/QueryParametersValidationTest.java @@ -14,6 +14,8 @@ import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; + /** * @author Andrea Boriero */ @@ -40,6 +42,17 @@ public void testSetParameterWithArrayWithNullElement(SessionFactoryScope scope) ); } + @Test + public void testSetParameterWithArrayWithNullElementWrongType(SessionFactoryScope scope) { + // SimpleEntity#id is of type Integer + assertThrows( IllegalArgumentException.class, () -> + scope.inTransaction( (session) -> + session.createQuery( "from EntityWithBasicArray e where e.strings = :p" ) + .setParameter( "p", new Integer[]{null, 1} ) + ) + ); + } + @Entity(name = "EntityWithBasicArray") public static class EntityWithBasicArray { @Id From 51c9d54ab31b13149af00cd156c63722aea2de3f Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 8 Dec 2025 22:06:59 +0100 Subject: [PATCH 063/181] more work on type inference/safety this time graphs and domain metamodel --- .../java/org/hibernate/SessionFactory.java | 2 + .../boot/model/NamedGraphCreator.java | 4 +- .../model/internal/NamedGraphCreatorJpa.java | 17 +++--- .../internal/NamedGraphCreatorParsed.java | 23 ++++---- .../boot/model/internal/NaturalIdBinder.java | 2 +- .../java/org/hibernate/graph/GraphParser.java | 52 ++++++++++--------- .../graph/internal/AttributeNodeImpl.java | 22 ++++---- .../hibernate/graph/internal/GraphImpl.java | 46 ++++++++-------- .../internal/parse/EntityNameResolver.java | 2 +- .../EntityNameResolverSessionFactory.java | 5 +- .../graph/internal/parse/GraphParser.java | 14 ++--- .../graph/internal/parse/GraphParsing.java | 49 ++++++++--------- .../internal/parse/PathQualifierType.java | 6 +-- .../domain/internal/AbstractManagedType.java | 5 +- .../internal/AbstractPluralAttribute.java | 9 ++-- .../AnyDiscriminatorSqmPathSource.java | 8 +-- .../internal/AnyMappingDomainTypeImpl.java | 19 +++---- .../model/domain/internal/ArrayTupleType.java | 2 +- .../domain/internal/BasicSqmPathSource.java | 10 ++-- .../domain/internal/EmbeddableTypeImpl.java | 1 + .../internal/EntityDiscriminatorSqmPath.java | 3 +- .../EntityPersisterConcurrentMap.java | 16 +++--- .../model/domain/internal/EntityTypeImpl.java | 3 +- .../domain/internal/JpaMetamodelImpl.java | 6 +-- .../internal/MappedSuperclassTypeImpl.java | 3 +- .../model/domain/internal/PathHelper.java | 5 +- .../internal/PluralAttributeBuilder.java | 4 +- .../internal/SingularAttributeImpl.java | 22 ++++---- 28 files changed, 178 insertions(+), 182 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/SessionFactory.java b/hibernate-core/src/main/java/org/hibernate/SessionFactory.java index c03e63bd2ad7..a90a43719c6f 100644 --- a/hibernate-core/src/main/java/org/hibernate/SessionFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/SessionFactory.java @@ -543,6 +543,7 @@ default RootGraph parseEntityGraph(Class rootEntityClass, CharSequence * * @since 7.0 */ + @Incubating default RootGraph parseEntityGraph(String rootEntityName, CharSequence graphText) { return GraphParser.parse( rootEntityName, graphText.toString(), unwrap( SessionFactoryImplementor.class ) ); } @@ -560,6 +561,7 @@ default RootGraph parseEntityGraph(String rootEntityName, CharSequence gr * * @since 7.0 */ + @Incubating default RootGraph parseEntityGraph(CharSequence graphText) { return GraphParser.parse( graphText.toString(), unwrap( SessionFactoryImplementor.class ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/NamedGraphCreator.java b/hibernate-core/src/main/java/org/hibernate/boot/model/NamedGraphCreator.java index 026af3d8f896..45e271aec6dd 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/NamedGraphCreator.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/NamedGraphCreator.java @@ -14,7 +14,7 @@ */ @FunctionalInterface public interface NamedGraphCreator { - RootGraphImplementor createEntityGraph( - Function, EntityDomainType> entityDomainClassResolver, + RootGraphImplementor createEntityGraph( + Function, EntityDomainType> entityDomainClassResolver, Function> entityDomainNameResolver); } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorJpa.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorJpa.java index 9aebd53de1c2..7321a8b65ef0 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorJpa.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorJpa.java @@ -6,6 +6,7 @@ import jakarta.persistence.NamedAttributeNode; import jakarta.persistence.NamedEntityGraph; +import org.checkerframework.checker.nullness.qual.NonNull; import org.hibernate.AnnotationException; import org.hibernate.boot.model.NamedGraphCreator; import org.hibernate.graph.internal.RootGraphImpl; @@ -36,16 +37,16 @@ class NamedGraphCreatorJpa implements NamedGraphCreator { } @Override - public RootGraphImplementor createEntityGraph( - Function, EntityDomainType> entityDomainClassResolver, + public RootGraphImplementor createEntityGraph( + Function, EntityDomainType> entityDomainClassResolver, Function> entityDomainNameResolver) { - //noinspection unchecked - final var rootEntityType = - (EntityDomainType) - entityDomainNameResolver.apply( jpaEntityName ); + return createGraph( (EntityDomainType) + entityDomainNameResolver.apply( jpaEntityName ) ); + } + + private @NonNull RootGraphImplementor createGraph(EntityDomainType rootEntityType) { final var entityGraph = createRootGraph( name, rootEntityType, annotation.includeAllAttributes() ); - final var subclassSubgraphs = annotation.subclassSubgraphs(); if ( subclassSubgraphs != null ) { for ( var subclassSubgraph : subclassSubgraphs ) { @@ -59,11 +60,9 @@ public RootGraphImplementor createEntityGraph( entityGraph.addTreatedSubgraph( subgraphType.asSubclass( graphJavaType ) ) ); } } - if ( annotation.attributeNodes() != null ) { applyNamedAttributeNodes( annotation.attributeNodes(), annotation, entityGraph ); } - return entityGraph; } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java index a264f3521fd2..f0646c26568f 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java @@ -41,18 +41,17 @@ class NamedGraphCreatorParsed implements NamedGraphCreator { } @Override - public RootGraphImplementor createEntityGraph( - Function, EntityDomainType> entityDomainClassResolver, + public RootGraphImplementor createEntityGraph( + Function, EntityDomainType> entityDomainClassResolver, Function> entityDomainNameResolver) { - final GraphLanguageLexer lexer = new GraphLanguageLexer( CharStreams.fromString( annotation.graph() ) ); - final GraphLanguageParser parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); - final GraphLanguageParser.GraphContext graphContext = parser.graph(); + final var lexer = new GraphLanguageLexer( CharStreams.fromString( annotation.graph() ) ); + final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); + final var graphContext = parser.graph(); - final EntityNameResolver entityNameResolver = new EntityNameResolver() { + final var entityNameResolver = new EntityNameResolver() { @Override - public EntityDomainType resolveEntityName(String entityName) { - //noinspection unchecked - final EntityDomainType entityDomainType = (EntityDomainType) entityDomainNameResolver.apply( entityName ); + public EntityDomainType resolveEntityName(String entityName) { + final var entityDomainType = (EntityDomainType) entityDomainNameResolver.apply( entityName ); if ( entityDomainType != null ) { return entityDomainType; } @@ -65,8 +64,7 @@ public EntityDomainType resolveEntityName(String entityName) { throw new InvalidGraphException( "Expecting graph text to include an entity name : " + annotation.graph() ); } final String jpaEntityName = graphContext.typeIndicator().TYPE_NAME().toString(); - //noinspection unchecked - final EntityDomainType entityDomainType = (EntityDomainType) entityDomainNameResolver.apply( jpaEntityName ); + final var entityDomainType = entityDomainNameResolver.apply( jpaEntityName ); final String name = this.name == null ? jpaEntityName : this.name; return GraphParsing.parse( name, entityDomainType, graphContext.attributeList(), entityNameResolver ); } @@ -74,8 +72,7 @@ public EntityDomainType resolveEntityName(String entityName) { if ( graphContext.typeIndicator() != null ) { throw new InvalidGraphException( "Expecting graph text to not include an entity name : " + annotation.graph() ); } - //noinspection unchecked - final EntityDomainType entityDomainType = (EntityDomainType) entityDomainClassResolver.apply( (Class) entityType ); + final var entityDomainType = entityDomainClassResolver.apply( entityType ); final String name = this.name == null ? entityDomainType.getName() : this.name; return GraphParsing.parse( name, entityDomainType, graphContext.attributeList(), entityNameResolver ); } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NaturalIdBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NaturalIdBinder.java index 626c1d6d4081..98d5f8a5d6ab 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NaturalIdBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NaturalIdBinder.java @@ -53,7 +53,7 @@ private static Identifier uniqueKeyName(MetadataBuildingContext context, Annotat private static void addColumnsToUniqueKey(AnnotatedColumns columns, Identifier name) { final var collector = columns.getBuildingContext().getMetadataCollector(); - final Table table = columns.getTable(); + final var table = columns.getTable(); final var uniqueKey = table.getOrCreateUniqueKey( name.render( collector.getDatabase().getDialect() ) ); final var property = columns.resolveProperty(); if ( property.isComposite() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/graph/GraphParser.java b/hibernate-core/src/main/java/org/hibernate/graph/GraphParser.java index 0bfcb46624ad..690246372dea 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/GraphParser.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/GraphParser.java @@ -9,6 +9,7 @@ import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.Subgraph; +import org.hibernate.Incubating; import org.hibernate.SessionFactory; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; @@ -89,6 +90,7 @@ public static RootGraph parse( * * @since 7.0 */ + @Incubating public static RootGraph parse( final String rootEntityName, final CharSequence graphText, @@ -96,7 +98,8 @@ public static RootGraph parse( if ( graphText == null ) { return null; } - return GraphParsing.parse( + //noinspection unchecked + return (RootGraph) GraphParsing.parse( rootEntityName, graphText.toString(), sessionFactory.unwrap( SessionFactoryImplementor.class ) @@ -117,16 +120,18 @@ public static RootGraph parse( * * @since 7.0 */ - public static RootGraph parse( + @Incubating + public static RootGraph parse( final CharSequence graphText, final SessionFactory sessionFactory) { if ( graphText == null ) { return null; } - return GraphParsing.parse( + //noinspection unchecked + return (RootGraph) GraphParsing.parse( graphText.toString(), sessionFactory.unwrap( SessionFactoryImplementor.class ) - ); + ); } /** @@ -153,7 +158,8 @@ public static RootGraph parse( return GraphParsing.parse( rootType, graphText.toString(), - entityManager.getEntityManagerFactory().unwrap( SessionFactoryImplementor.class ) + entityManager.getEntityManagerFactory() + .unwrap( SessionFactoryImplementor.class ) ); } @@ -190,13 +196,12 @@ public static void parseInto( * * @throws InvalidGraphException if the textual representation is invalid. */ - @SuppressWarnings("unchecked") public static void parseInto( - final EntityGraph graph, + final EntityGraph graph, final CharSequence graphText, final EntityManager entityManager) { parseInto( - (GraphImplementor) graph, + (GraphImplementor) graph, graphText, ( (SessionImplementor) entityManager ).getSessionFactory() ); @@ -211,13 +216,12 @@ public static void parseInto( * * @throws InvalidGraphException if the textual representation is invalid. */ - @SuppressWarnings("unchecked") - public static void parseInto( - final Subgraph graph, + public static void parseInto( + final Subgraph graph, final CharSequence graphText, final EntityManager entityManager) { parseInto( - (GraphImplementor) graph, + (GraphImplementor) graph, graphText, ( (SessionImplementor) entityManager ).getSessionFactory() ); @@ -232,12 +236,12 @@ public static void parseInto( * * @throws InvalidGraphException if the textual representation is invalid. */ - public static void parseInto( - final Graph graph, + public static void parseInto( + final Graph graph, final CharSequence graphText, final EntityManagerFactory entityManagerFactory) { parseInto( - (GraphImplementor) graph, + (GraphImplementor) graph, graphText, (SessionFactoryImplementor) entityManagerFactory ); @@ -252,13 +256,12 @@ public static void parseInto( * * @throws InvalidGraphException if the textual representation is invalid. */ - @SuppressWarnings("unchecked") - public static void parseInto( - final EntityGraph graph, + public static void parseInto( + final EntityGraph graph, final CharSequence graphText, final EntityManagerFactory entityManagerFactory) { parseInto( - (GraphImplementor) graph, + (GraphImplementor) graph, graphText, (SessionFactoryImplementor) entityManagerFactory ); @@ -273,13 +276,12 @@ public static void parseInto( * * @throws InvalidGraphException if the textual representation is invalid. */ - @SuppressWarnings("unchecked") - public static void parseInto( - final Subgraph graph, + public static void parseInto( + final Subgraph graph, final CharSequence graphText, final EntityManagerFactory entityManagerFactory) { parseInto( - (GraphImplementor) graph, + (GraphImplementor) graph, graphText, (SessionFactoryImplementor) entityManagerFactory ); @@ -298,8 +300,8 @@ public static void parseInto( * * @throws InvalidGraphException if the textual representation is invalid. */ - private static void parseInto( - GraphImplementor graph, + private static void parseInto( + GraphImplementor graph, final CharSequence graphText, SessionFactoryImplementor sessionFactory) { if ( graphText != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/AttributeNodeImpl.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/AttributeNodeImpl.java index 6856698ea440..dd25ee52bc47 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/AttributeNodeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/AttributeNodeImpl.java @@ -258,13 +258,13 @@ public SubGraphImplementor makeSubGraph() { @Override @Deprecated public SubGraphImplementor makeSubGraph(Class subtype) { - final ManagedDomainType managedType = asManagedType( valueGraphType ); + final var managedType = asManagedType( valueGraphType ); if ( !managedType.getJavaType().isAssignableFrom( subtype ) ) { throw new IllegalArgumentException( "Not a subtype: " + subtype.getName() ); } @SuppressWarnings("unchecked") - final Class castSuptype = (Class) subtype; - final SubGraphImplementor result = makeSubGraph().addTreatedSubgraph( castSuptype ); + final var castSuptype = (Class) subtype; + final var result = makeSubGraph().addTreatedSubgraph( castSuptype ); //noinspection unchecked return (SubGraphImplementor) result; } @@ -282,13 +282,13 @@ public SubGraphImplementor makeKeySubGraph() { @Override @Deprecated public SubGraphImplementor makeKeySubGraph(Class subtype) { checkMap(); - final ManagedDomainType type = asManagedType( keyGraphType ); + final var type = asManagedType( keyGraphType ); if ( !type.getJavaType().isAssignableFrom( subtype ) ) { throw new IllegalArgumentException( "Not a key subtype: " + subtype.getName() ); } @SuppressWarnings("unchecked") - final Class castType = (Class) subtype; - final SubGraphImplementor result = makeKeySubGraph().addTreatedSubgraph( castType ); + final var castType = (Class) subtype; + final var result = makeKeySubGraph().addTreatedSubgraph( castType ); //noinspection unchecked return (SubGraphImplementor) result; } @@ -323,7 +323,7 @@ public String toString() { public void merge(AttributeNodeImplementor that) { assert that.isMutable() == isMutable(); assert that.getAttributeDescriptor() == attribute; - final SubGraphImplementor otherValueSubgraph = that.getValueSubgraph(); + final var otherValueSubgraph = that.getValueSubgraph(); if ( otherValueSubgraph != null ) { if ( valueSubgraph == null ) { valueSubgraph = otherValueSubgraph.makeCopy( isMutable() ); @@ -333,7 +333,7 @@ public void merge(AttributeNodeImplementor that) { valueSubgraph.mergeInternal( otherValueSubgraph ); } } - final SubGraphImplementor otherKeySubgraph = that.getKeySubgraph(); + final var otherKeySubgraph = that.getKeySubgraph(); if ( otherKeySubgraph != null ) { if ( keySubgraph == null ) { keySubgraph = otherKeySubgraph.makeCopy( isMutable() ); @@ -351,7 +351,8 @@ public Map, SubGraphImplementor> getSubGraphs() { return emptyMap(); } else { - final HashMap, SubGraphImplementor> map = new HashMap<>( valueSubgraph.getTreatedSubgraphs() ); + final HashMap, SubGraphImplementor> map = + new HashMap<>( valueSubgraph.getTreatedSubgraphs() ); map.put( attribute.getValueGraphType().getJavaType(), valueSubgraph ); return map; } @@ -363,7 +364,8 @@ public Map, SubGraphImplementor> getKeySubGraphs() { return emptyMap(); } else { - final HashMap, SubGraphImplementor> map = new HashMap<>( keySubgraph.getTreatedSubgraphs() ); + final HashMap, SubGraphImplementor> map = + new HashMap<>( keySubgraph.getTreatedSubgraphs() ); map.put( attribute.getKeyGraphType().getJavaType(), keySubgraph ); return map; } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/GraphImpl.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/GraphImpl.java index b28471f203f8..0a78778d88e9 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/GraphImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/GraphImpl.java @@ -156,19 +156,19 @@ public void mergeInternal(GraphImplementor graph) { } private void mergeNode(PersistentAttribute attribute, AttributeNodeImplementor node) { - final AttributeNodeImplementor existingNode = getNodeForPut( attribute ); + final var existingNode = getNodeForPut( attribute ); if ( existingNode == null ) { attributeNodes.put( attribute, node.makeCopy( isMutable() ) ); } else { - // keep the local one, but merge in the incoming one + // keep the local one but merge in the incoming one mergeNode( node, existingNode ); } } private void mergeGraph(SubGraphImplementor subgraph) { - final Class javaType = subgraph.getClassType(); - final SubGraphImplementor existing = getTreatedSubgraphForPut( javaType ); + final var javaType = subgraph.getClassType(); + final var existing = getTreatedSubgraphForPut( javaType ); if ( existing == null ) { treatedSubgraphs.put( javaType, subgraph.makeCopy( isMutable() ) ); } @@ -197,12 +197,12 @@ public List> getAttributeNodeList() { @Override public AttributeNodeImplementor findAttributeNode(String attributeName) { - final PersistentAttribute attribute = findAttributeInSupertypes( attributeName ); + final var attribute = findAttributeInSupertypes( attributeName ); @SuppressWarnings("unchecked") // The JPA API is unsafe by nature - final PersistentAttribute persistentAttribute = (PersistentAttribute) attribute; - final AttributeNodeImplementor node = attribute == null ? null : findAttributeNode( persistentAttribute ); + final var persistentAttribute = (PersistentAttribute) attribute; + final var node = attribute == null ? null : findAttributeNode( persistentAttribute ); if ( node == null && treatedSubgraphs != null ) { - for ( SubGraphImplementor subgraph : treatedSubgraphs.values() ) { + for ( var subgraph : treatedSubgraphs.values() ) { final AttributeNodeImplementor subgraphNode = subgraph.findAttributeNode( attributeName ); if ( subgraphNode != null ) { return subgraphNode; @@ -264,7 +264,7 @@ public void addAttributeNodes(String... attributeNames) { @Override @SafeVarargs public final void addAttributeNodes(Attribute... attributes) { - for ( Attribute attribute : attributes ) { + for ( var attribute : attributes ) { addAttributeNode( attribute ); } } @@ -290,9 +290,9 @@ public void removeAttributeNodes(Attribute.PersistentAttributeType nodeType) { @Override public AttributeNodeImplementor findOrCreateAttributeNode(PersistentAttribute attribute) { verifyMutability(); - final AttributeNodeImplementor node = getNodeForPut( attribute ); + final var node = getNodeForPut( attribute ); if ( node == null ) { - final AttributeNodeImplementor newAttrNode = AttributeNodeImpl.create( attribute, isMutable() ); + final var newAttrNode = AttributeNodeImpl.create( attribute, isMutable() ); attributeNodes.put( attribute, newAttrNode ); return newAttrNode; } @@ -303,9 +303,9 @@ public AttributeNodeImplementor findOrCreateAttributeNode(Persisten private AttributeNodeImplementor findOrCreateAttributeNode(PluralPersistentAttribute attribute) { verifyMutability(); - final AttributeNodeImplementor node = getNodeForPut( attribute ); + final var node = getNodeForPut( attribute ); if ( node == null ) { - final AttributeNodeImplementor newAttrNode = AttributeNodeImpl.create( attribute, isMutable() ); + final var newAttrNode = AttributeNodeImpl.create( attribute, isMutable() ); attributeNodes.put( attribute, newAttrNode ); return newAttrNode; } @@ -316,9 +316,9 @@ private AttributeNodeImplementor findOrCreateAttributeNode(PluralPe private AttributeNodeImplementor,V,K> findOrCreateAttributeNode(MapPersistentAttribute attribute) { verifyMutability(); - final AttributeNodeImplementor,V,K> node = getNodeForPut( attribute ); + final var node = getNodeForPut( attribute ); if ( node == null ) { - final AttributeNodeImplementor,V,K> newAttrNode = AttributeNodeImpl.create( attribute, isMutable() ); + final var newAttrNode = AttributeNodeImpl.create( attribute, isMutable() ); attributeNodes.put( attribute, newAttrNode ); return newAttrNode; } @@ -329,21 +329,21 @@ private AttributeNodeImplementor,V,K> findOrCreateAttributeNode(M @Override public AttributeNodeImplementor findOrCreateAttributeNode(String attributeName) { - final PersistentAttribute attribute = getAttribute( attributeName ); + final var attribute = getAttribute( attributeName ); @SuppressWarnings("unchecked") // The JPA API is unsafe by nature - final PersistentAttribute persistentAttribute = (PersistentAttribute) attribute; + final var persistentAttribute = (PersistentAttribute) attribute; return findOrCreateAttributeNode( persistentAttribute ); } private PersistentAttribute findAttributeInSupertypes(String attributeName) { - final PersistentAttribute attribute = managedType.findAttributeInSuperTypes( attributeName ); + final var attribute = managedType.findAttributeInSuperTypes( attributeName ); return attribute instanceof SqmPathSource sqmPathSource && sqmPathSource.isGeneric() ? managedType.findConcreteGenericAttribute( attributeName ) : attribute; } private PersistentAttribute getAttribute(String attributeName) { - final PersistentAttribute attribute = managedType.getAttribute( attributeName ); + final var attribute = managedType.getAttribute( attributeName ); return attribute instanceof SqmPathSource sqmPathSource && sqmPathSource.isGeneric() ? managedType.findConcreteGenericAttribute( attributeName ) : attribute; @@ -470,10 +470,10 @@ public SubGraphImplementor addTreatedSubgraph(ManagedType ty return (SubGraphImplementor) this; } else { - final Class javaType = type.getJavaType(); - final SubGraphImplementor castSubgraph = getTreatedSubgraphForPut( javaType ); + final var javaType = type.getJavaType(); + final var castSubgraph = getTreatedSubgraphForPut( javaType ); if ( castSubgraph == null ) { - final SubGraphImpl subgraph = new SubGraphImpl<>( (ManagedDomainType) type, true ); + final var subgraph = new SubGraphImpl<>( (ManagedDomainType) type, true ); treatedSubgraphs.put( javaType, subgraph ); return subgraph; } @@ -495,7 +495,7 @@ public Map, SubGraphImplementor> getTreatedSubgr @Override public String toString() { - final StringBuilder builder = new StringBuilder( "Graph[" ).append( managedType.getTypeName() ); + final var builder = new StringBuilder( "Graph[" ).append( managedType.getTypeName() ); if ( attributeNodes != null ) { builder.append( ", nodes=" ) .append( attributeNodes.values().stream() diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolver.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolver.java index 10073e2468b4..d1bfce497a59 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolver.java @@ -11,5 +11,5 @@ */ @FunctionalInterface public interface EntityNameResolver { - EntityDomainType resolveEntityName(String entityName); + EntityDomainType resolveEntityName(String entityName); } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolverSessionFactory.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolverSessionFactory.java index 88f601c9d6d0..1a7e8a43c074 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolverSessionFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolverSessionFactory.java @@ -18,8 +18,7 @@ public EntityNameResolverSessionFactory(SessionFactoryImplementor sessionFactory } @Override - public EntityDomainType resolveEntityName(String entityName) { - //noinspection unchecked - return (EntityDomainType) sessionFactory.getJpaMetamodel().findEntityType( entityName ); + public EntityDomainType resolveEntityName(String entityName) { + return sessionFactory.getJpaMetamodel().findEntityType( entityName ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java index e02e5f06ee03..7953808c610f 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java @@ -81,7 +81,7 @@ public AttributeNodeImplementor visitAttributeNode(GraphLanguageParser.At subGraphCreator = pathQualifierType.getSubGraphCreator(); } - final AttributeNodeImplementor attributeNode = resolveAttributeNode( attributeName ); + final var attributeNode = resolveAttributeNode( attributeName ); if ( attributeNodeContext.subGraph() != null ) { attributeNodeStack.push( attributeNode ); @@ -109,10 +109,10 @@ public AttributeNodeImplementor visitAttributeNode(GraphLanguageParser.At } private AttributeNodeImplementor resolveAttributeNode(String attributeName) { - final GraphImplementor currentGraph = graphStack.getCurrent(); + final var currentGraph = graphStack.getCurrent(); assert currentGraph != null; - final AttributeNodeImplementor attributeNode = currentGraph.findOrCreateAttributeNode( attributeName ); + final var attributeNode = currentGraph.findOrCreateAttributeNode( attributeName ); assert attributeNode != null; return attributeNode; @@ -132,7 +132,9 @@ private PathQualifierType resolvePathQualifier(String qualifier) { @Override public SubGraphImplementor visitSubGraph(GraphLanguageParser.SubGraphContext subGraphContext) { - final String subTypeName = subGraphContext.typeIndicator() == null ? null : subGraphContext.typeIndicator().TYPE_NAME().getText(); + final String subTypeName = + subGraphContext.typeIndicator() == null ? null + : subGraphContext.typeIndicator().TYPE_NAME().getText(); if ( PARSING_LOGGER.isTraceEnabled() ) { PARSING_LOGGER.tracef( @@ -142,8 +144,8 @@ public SubGraphImplementor visitSubGraph(GraphLanguageParser.SubGraphContext ); } - final AttributeNodeImplementor attributeNode = attributeNodeStack.getCurrent(); - final SubGraphGenerator subGraphCreator = graphSourceStack.getCurrent(); + final var attributeNode = attributeNodeStack.getCurrent(); + final var subGraphCreator = graphSourceStack.getCurrent(); final SubGraphImplementor subGraph = subGraphCreator.createSubGraph( attributeNode, diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java index 2c8be74ef601..d1d1ecb77ae2 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java @@ -30,9 +30,9 @@ public static RootGraphImplementor parse( return null; } - final GraphLanguageLexer lexer = new GraphLanguageLexer( CharStreams.fromString( graphText ) ); - final GraphLanguageParser parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); - final GraphLanguageParser.GraphContext graphContext = parser.graph(); + final var lexer = new GraphLanguageLexer( CharStreams.fromString( graphText ) ); + final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); + final var graphContext = parser.graph(); if ( graphContext.typeIndicator() != null ) { // todo : an alternative here would be to simply validate that the entity type @@ -40,7 +40,7 @@ public static RootGraphImplementor parse( throw new InvalidGraphException( "Expecting graph text to not include an entity name : " + graphText ); } - final EntityDomainType entityType = sessionFactory.getJpaMetamodel().entity( entityClass ); + final var entityType = sessionFactory.getJpaMetamodel().entity( entityClass ); return parse( entityType, graphContext.attributeList(), sessionFactory ); } @@ -52,9 +52,9 @@ public static RootGraphImplementor parse( return null; } - final GraphLanguageLexer lexer = new GraphLanguageLexer( CharStreams.fromString( graphText ) ); - final GraphLanguageParser parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); - final GraphLanguageParser.GraphContext graphContext = parser.graph(); + final var lexer = new GraphLanguageLexer( CharStreams.fromString( graphText ) ); + final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); + final var graphContext = parser.graph(); if ( graphContext.typeIndicator() != null ) { // todo : an alternative here would be to simply validate that the entity type @@ -65,7 +65,7 @@ public static RootGraphImplementor parse( return parse( entityDomainType, graphContext.attributeList(), sessionFactory ); } - public static RootGraphImplementor parse( + public static RootGraphImplementor parse( String entityName, String graphText, SessionFactoryImplementor sessionFactory) { @@ -73,9 +73,9 @@ public static RootGraphImplementor parse( return null; } - final GraphLanguageLexer lexer = new GraphLanguageLexer( CharStreams.fromString( graphText ) ); - final GraphLanguageParser parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); - final GraphLanguageParser.GraphContext graphContext = parser.graph(); + final var lexer = new GraphLanguageLexer( CharStreams.fromString( graphText ) ); + final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); + final var graphContext = parser.graph(); if ( graphContext.typeIndicator() != null ) { // todo : an alternative here would be to simply validate that the entity type @@ -83,30 +83,27 @@ public static RootGraphImplementor parse( throw new InvalidGraphException( "Expecting graph text to not include an entity name : " + graphText ); } - //noinspection unchecked - final EntityDomainType entityType = (EntityDomainType) sessionFactory.getJpaMetamodel().entity( entityName ); + final var entityType = sessionFactory.getJpaMetamodel().entity( entityName ); return parse( entityType, graphContext.attributeList(), sessionFactory ); } - public static RootGraphImplementor parse( + public static RootGraphImplementor parse( String graphText, SessionFactoryImplementor sessionFactory) { if ( graphText == null ) { return null; } - final GraphLanguageLexer lexer = new GraphLanguageLexer( CharStreams.fromString( graphText ) ); - final GraphLanguageParser parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); - final GraphLanguageParser.GraphContext graphContext = parser.graph(); + final var lexer = new GraphLanguageLexer( CharStreams.fromString( graphText ) ); + final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); + final var graphContext = parser.graph(); if ( graphContext.typeIndicator() == null ) { throw new InvalidGraphException( "Expecting graph text to include an entity name : " + graphText ); } final String entityName = graphContext.typeIndicator().TYPE_NAME().getText(); - - //noinspection unchecked - final EntityDomainType entityType = (EntityDomainType) sessionFactory.getJpaMetamodel().entity( entityName ); + final var entityType = sessionFactory.getJpaMetamodel().entity( entityName ); return parse( entityType, graphContext.attributeList(), sessionFactory ); } @@ -114,7 +111,7 @@ public static RootGraphImplementor parse( EntityDomainType rootType, GraphLanguageParser.AttributeListContext attributeListContext, SessionFactoryImplementor sessionFactory) { - return parse( rootType, attributeListContext, new EntityNameResolverSessionFactory( sessionFactory ) ); + return parse( rootType, attributeListContext, sessionFactory.getJpaMetamodel()::findEntityType ); } public static RootGraphImplementor parse( @@ -131,7 +128,7 @@ public static RootGraphImplementor parse( EntityNameResolver entityNameResolver) { final RootGraphImpl targetGraph = new RootGraphImpl<>( name, rootType ); - final GraphParser visitor = new GraphParser( entityNameResolver ); + final var visitor = new GraphParser( entityNameResolver ); visitor.getGraphStack().push( targetGraph ); try { visitor.visitAttributeList( attributeListContext ); @@ -153,9 +150,9 @@ public static void parseInto( GraphImplementor targetGraph, CharSequence graphString, SessionFactoryImplementor sessionFactory) { - final GraphLanguageLexer lexer = new GraphLanguageLexer( CharStreams.fromString( graphString.toString() ) ); - final GraphLanguageParser parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); - final GraphLanguageParser.GraphContext graphContext = parser.graph(); + final var lexer = new GraphLanguageLexer( CharStreams.fromString( graphString.toString() ) ); + final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); + final var graphContext = parser.graph(); if ( graphContext.typeIndicator() != null ) { // todo : throw an exception? Log warning? Ignore? @@ -163,7 +160,7 @@ public static void parseInto( } // Build an instance of this class as a visitor - final GraphParser visitor = new GraphParser( sessionFactory ); + final var visitor = new GraphParser( sessionFactory ); visitor.getGraphStack().push( targetGraph ); try { diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/PathQualifierType.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/PathQualifierType.java index 3687da5df37f..24a5fa062ecb 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/PathQualifierType.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/PathQualifierType.java @@ -5,7 +5,6 @@ package org.hibernate.graph.internal.parse; -import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.ManagedDomainType; /** @@ -24,11 +23,12 @@ public enum PathQualifierType { ); private static ManagedDomainType managedType(String subtypeName, EntityNameResolver entityNameResolver) { - final EntityDomainType entityDomainType = entityNameResolver.resolveEntityName( subtypeName ); + final var entityDomainType = entityNameResolver.resolveEntityName( subtypeName ); if ( entityDomainType == null ) { throw new IllegalArgumentException( "Unknown managed type: " + subtypeName ); } - return entityDomainType; + //noinspection unchecked + return (ManagedDomainType) entityDomainType; } private final SubGraphGenerator subGraphCreator; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractManagedType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractManagedType.java index f9c319934dd6..25554e1a5d4b 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractManagedType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractManagedType.java @@ -126,8 +126,9 @@ public RepresentationMode getRepresentationMode() { @Override public void visitAttributes(Consumer> action) { visitDeclaredAttributes( action ); - if ( getSuperType() != null ) { - getSuperType().visitAttributes( action ); + final var superType = getSuperType(); + if ( superType != null ) { + superType.visitAttributes( action ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractPluralAttribute.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractPluralAttribute.java index 8fcf2003a79a..4c085a9b4546 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractPluralAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractPluralAttribute.java @@ -125,8 +125,9 @@ public SimpleDomainType getKeyGraphType() { @Override public boolean isAssociation() { - return getPersistentAttributeType() == PersistentAttributeType.ONE_TO_MANY - || getPersistentAttributeType() == PersistentAttributeType.MANY_TO_MANY; + final var persistentAttributeType = getPersistentAttributeType(); + return persistentAttributeType == PersistentAttributeType.ONE_TO_MANY + || persistentAttributeType == PersistentAttributeType.MANY_TO_MANY; } @Override @@ -147,8 +148,8 @@ public Class getBindableJavaType() { @SuppressWarnings("unchecked") @Override public SqmPath createSqmPath(SqmPath lhs, @Nullable SqmPathSource intermediatePathSource) { - // We need an unchecked cast here : PluralPersistentAttribute implements path source with its element type - // but resolving paths from it must produce collection-typed expressions. + // We need an unchecked cast here: PluralPersistentAttribute implements PathSource with + // its element type, but resolving paths from it must produce collection-typed expressions. return (SqmPath) new SqmPluralValuedSimplePath<>( PathHelper.append( lhs, this, intermediatePathSource ), this, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPathSource.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPathSource.java index a49b01080e81..64ea52e4f7d9 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPathSource.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyDiscriminatorSqmPathSource.java @@ -12,7 +12,6 @@ import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.tree.domain.SqmDomainType; import org.hibernate.query.sqm.tree.domain.SqmPath; -import org.hibernate.spi.NavigablePath; import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.java.JavaType; @@ -38,10 +37,11 @@ public AnyDiscriminatorSqmPathSource( @Override public SqmPath createSqmPath(SqmPath lhs, @Nullable SqmPathSource intermediatePathSource) { - final NavigablePath navigablePath = + final var path = lhs.getNavigablePath(); + final var navigablePath = intermediatePathSource == null - ? lhs.getNavigablePath() - : lhs.getNavigablePath().append( intermediatePathSource.getPathName() ); + ? path + : path.append( intermediatePathSource.getPathName() ); return new AnyDiscriminatorSqmPath<>( navigablePath, pathModel, lhs, lhs.nodeBuilder() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyMappingDomainTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyMappingDomainTypeImpl.java index 81e7b9977877..1145ed937e8a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyMappingDomainTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AnyMappingDomainTypeImpl.java @@ -6,7 +6,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.mapping.Any; -import org.hibernate.mapping.Column; import org.hibernate.metamodel.model.domain.AnyMappingDomainType; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.SimpleDomainType; @@ -18,8 +17,6 @@ import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.internal.ConvertedBasicTypeImpl; -import java.util.List; - import static jakarta.persistence.metamodel.Type.PersistenceType.ENTITY; import static org.hibernate.metamodel.mapping.internal.AnyDiscriminatorPart.determineDiscriminatorConverter; @@ -40,10 +37,9 @@ public AnyMappingDomainTypeImpl( this.anyType = anyType; this.baseJtd = baseJtd; - final MetaType discriminatorType = (MetaType) anyType.getDiscriminatorType(); - final BasicType discriminatorBaseType = (BasicType) discriminatorType.getBaseType(); - final NavigableRole navigableRole = resolveNavigableRole( bootAnyMapping ); - + final var discriminatorType = (MetaType) anyType.getDiscriminatorType(); + final var discriminatorBaseType = (BasicType) discriminatorType.getBaseType(); + final var navigableRole = resolveNavigableRole( bootAnyMapping ); anyDiscriminatorType = new ConvertedBasicTypeImpl( navigableRole.getFullPath(), discriminatorBaseType.getJdbcType(), @@ -73,13 +69,14 @@ public String getTypeName() { } private NavigableRole resolveNavigableRole(Any bootAnyMapping) { - final StringBuilder buffer = new StringBuilder(); - if ( bootAnyMapping.getTable() != null ) { - buffer.append( bootAnyMapping.getTable().getName() ); + final var buffer = new StringBuilder(); + final var table = bootAnyMapping.getTable(); + if ( table != null ) { + buffer.append( table.getName() ); } buffer.append( "(" ); - final List columns = bootAnyMapping.getColumns(); + final var columns = bootAnyMapping.getColumns(); for ( int i = 0; i < columns.size(); i++ ) { buffer.append( columns.get( i ).getName() ); if ( i+1 < columns.size() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/ArrayTupleType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/ArrayTupleType.java index f25f1eb7e394..c06524882631 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/ArrayTupleType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/ArrayTupleType.java @@ -53,7 +53,7 @@ public String getTypeName() { } private static JavaType[] getTypeDescriptors(SqmExpressible[] components) { - final JavaType[] typeDescriptors = new JavaType[components.length]; + final var typeDescriptors = new JavaType[components.length]; for ( int i = 0; i < components.length; i++ ) { typeDescriptors[i] = components[i].getExpressibleJavaType(); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/BasicSqmPathSource.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/BasicSqmPathSource.java index cd699971ba16..6b909090f748 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/BasicSqmPathSource.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/BasicSqmPathSource.java @@ -48,8 +48,8 @@ public String getTypeName() { @Override public @Nullable SqmPathSource findSubPathSource(String name) { - String path = pathModel.getPathName(); - String pathDesc = path == null || path.startsWith( "{" ) ? " " : " '" + pathModel.getPathName() + "' "; + final String path = pathModel.getPathName(); + final String pathDesc = path == null || path.startsWith( "{" ) ? " " : " '" + pathModel.getPathName() + "' "; throw new TerminalPathException( "Terminal path" + pathDesc + "has no attribute '" + name + "'" ); } @@ -85,8 +85,8 @@ public boolean isGeneric() { @Override public String toString() { - return "BasicSqmPathSource(" + - getPathName() + " : " + getJavaType().getSimpleName() + - ")"; + return "BasicSqmPathSource(" + + getPathName() + " : " + getJavaType().getSimpleName() + + ")"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddableTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddableTypeImpl.java index 223e6ee38cc1..d308d62873c0 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddableTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EmbeddableTypeImpl.java @@ -34,6 +34,7 @@ public class EmbeddableTypeImpl extends AbstractManagedType implements SqmEmbeddableDomainType, Serializable { + @SuppressWarnings("FieldCanBeLocal") private final boolean isDynamic; private final EmbeddedDiscriminatorSqmPathSource discriminatorPathSource; private final List> subtypes = new ArrayList<>(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityDiscriminatorSqmPath.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityDiscriminatorSqmPath.java index 4abc6181c474..093b7d59238e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityDiscriminatorSqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityDiscriminatorSqmPath.java @@ -80,6 +80,7 @@ public EntityDiscriminatorSqmPath copy(SqmCopyContext context) { public X accept(SemanticQueryWalker walker) { return entityDescriptor.hasSubclasses() ? walker.visitDiscriminatorPath( this ) - : walker.visitEntityTypeLiteralExpression( new SqmLiteralEntityType( entityDomainType, nodeBuilder() ) ); + : walker.visitEntityTypeLiteralExpression( + new SqmLiteralEntityType( entityDomainType, nodeBuilder() ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityPersisterConcurrentMap.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityPersisterConcurrentMap.java index db13d7b2e468..85526604475c 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityPersisterConcurrentMap.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityPersisterConcurrentMap.java @@ -7,10 +7,11 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; import org.hibernate.persister.entity.EntityPersister; +import static java.util.stream.Collectors.toUnmodifiableMap; + /** * Concurrent Map implementation of mappings entity name -> EntityPersister. * Concurrency is optimised for read operations; write operations will @@ -26,10 +27,7 @@ public final class EntityPersisterConcurrentMap { public EntityPersister get(final String name) { final var entityPersisterHolder = map.get( name ); - if ( entityPersisterHolder != null ) { - return entityPersisterHolder.entityPersister; - } - return null; + return entityPersisterHolder == null ? null : entityPersisterHolder.entityPersister; } public EntityPersister[] values() { @@ -55,10 +53,10 @@ public String[] keys() { } private void recomputeValues() { - //Assumption: the write lock is being held (synchronize on this) + //Assumption: the write lock is held (synchronize on this) final int size = map.size(); - final EntityPersister[] newValues = new EntityPersister[size]; - final String[] newKeys = new String[size]; + final var newValues = new EntityPersister[size]; + final var newKeys = new String[size]; int i = 0; for ( var entry : map.entrySet() ) { newValues[i] = entry.getValue().entityPersister; @@ -75,7 +73,7 @@ private void recomputeValues() { */ @Deprecated(forRemoval = true) public Map convertToMap() { - return map.entrySet().stream().collect( Collectors.toUnmodifiableMap( + return map.entrySet().stream().collect( toUnmodifiableMap( Map.Entry::getKey, e -> e.getValue().entityPersister ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityTypeImpl.java index 0fa0b6261c57..6a7a91821778 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityTypeImpl.java @@ -23,7 +23,6 @@ import org.hibernate.metamodel.model.domain.IdentifiableDomainType; import org.hibernate.metamodel.model.domain.JpaMetamodel; import org.hibernate.metamodel.model.domain.ManagedDomainType; -import org.hibernate.metamodel.model.domain.PersistentAttribute; import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor; import org.hibernate.query.PathException; import org.hibernate.query.sqm.SqmPathSource; @@ -153,7 +152,7 @@ public SqmEntityDomainType getPathType() { @Override public @Nullable SqmPathSource findSubPathSource(String name) { - final PersistentAttribute attribute = super.findAttribute( name ); + final var attribute = super.findAttribute( name ); if ( attribute != null ) { return (SqmPathSource) attribute; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java index f0ffc1f23837..ef0b6eafad14 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java @@ -355,7 +355,7 @@ public > E enumValue(EnumJavaType enumType, String enumValu @Override public JavaType getJavaConstantType(String className, String fieldName) { try { - final Field referencedField = getJavaField( className, fieldName ); + final var referencedField = getJavaField( className, fieldName ); if ( referencedField != null ) { return getTypeConfiguration().getJavaTypeRegistry() .resolveDescriptor( referencedField.getType() ); @@ -370,7 +370,7 @@ public JavaType getJavaConstantType(String className, String fieldName) { @Override public T getJavaConstant(String className, String fieldName) { try { - final Field referencedField = getJavaField( className, fieldName ); + final var referencedField = getJavaField( className, fieldName ); //noinspection unchecked return (T) referencedField.get( null ); } @@ -490,7 +490,7 @@ private void applyNamedEntityGraphs(Collection named CORE_LOGGER.tracef( "Applying named entity graph [name=%s, source=%s]", definition.name(), definition.source() ); - final RootGraphImplementor graph = definition.graphCreator().createEntityGraph( + final var graph = definition.graphCreator().createEntityGraph( (entityClass) -> { if ( managedTypeByClass.get( entityClass ) instanceof EntityDomainType match ) { return match; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java index 13549780a83f..4f5cb67470a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java @@ -9,7 +9,6 @@ import org.hibernate.metamodel.UnsupportedMappingException; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.model.domain.IdentifiableDomainType; -import org.hibernate.metamodel.model.domain.PersistentAttribute; import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.tree.domain.SqmDomainType; @@ -89,7 +88,7 @@ public SqmMappedSuperclassDomainType getPathType() { @Override public @Nullable SqmPathSource findSubPathSource(String name) { - final PersistentAttribute attribute = findAttribute( name ); + final var attribute = findAttribute( name ); if ( attribute != null ) { return (SqmPathSource) attribute; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/PathHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/PathHelper.java index b9889b827173..3a30d128b970 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/PathHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/PathHelper.java @@ -11,8 +11,9 @@ public class PathHelper { public static NavigablePath append(SqmPath lhs, SqmPathSource rhs, @Nullable SqmPathSource intermediatePathSource) { + final var navigablePath = lhs.getNavigablePath(); return intermediatePathSource == null - ? lhs.getNavigablePath().append( rhs.getPathName() ) - : lhs.getNavigablePath().append( intermediatePathSource.getPathName() ).append( rhs.getPathName() ); + ? navigablePath.append( rhs.getPathName() ) + : navigablePath.append( intermediatePathSource.getPathName() ).append( rhs.getPathName() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/PluralAttributeBuilder.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/PluralAttributeBuilder.java index a47add5cdde1..028144d45019 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/PluralAttributeBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/PluralAttributeBuilder.java @@ -86,7 +86,7 @@ public static PersistentAttribute build( attributeMetadata.getMember() ); - final Class javaClass = attributeJtd.getJavaTypeClass(); + final var javaClass = attributeJtd.getJavaTypeClass(); if ( Map.class.equals( javaClass ) ) { return new MapAttributeImpl( builder ); } @@ -124,7 +124,7 @@ else if ( Collection.class.isAssignableFrom( javaClass ) ) { private static SimpleDomainType determineListIndexOrMapKeyType( PluralAttributeMetadata attributeMetadata, MetadataContext metadataContext) { - final Class javaType = attributeMetadata.getJavaType(); + final var javaType = attributeMetadata.getJavaType(); if ( Map.class.isAssignableFrom( javaType ) ) { return (SimpleDomainType) determineSimpleType( attributeMetadata.getMapKeyValueContext(), metadataContext ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/SingularAttributeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/SingularAttributeImpl.java index 28f1f9c3565d..de34e2d38051 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/SingularAttributeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/SingularAttributeImpl.java @@ -17,11 +17,9 @@ import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; import org.hibernate.metamodel.model.domain.SimpleDomainType; import org.hibernate.query.SemanticException; -import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SqmBindableType; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.hql.spi.SqmCreationState; -import org.hibernate.query.sqm.internal.SqmMappingModelHelper; import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmSingularJoin; @@ -38,6 +36,7 @@ import org.hibernate.type.descriptor.java.JavaType; import static jakarta.persistence.metamodel.Bindable.BindableType.SINGULAR_ATTRIBUTE; +import static org.hibernate.query.sqm.internal.SqmMappingModelHelper.resolveSqmPathSource; import static org.hibernate.query.sqm.spi.SqmCreationHelper.buildSubNavigablePath; import static org.hibernate.query.sqm.spi.SqmCreationHelper.determineAlias; @@ -77,7 +76,7 @@ public SingularAttributeImpl( this.isVersion = isVersion; this.isOptional = isOptional; - this.sqmPathSource = SqmMappingModelHelper.resolveSqmPathSource( + this.sqmPathSource = resolveSqmPathSource( name, this, attributeType, @@ -157,15 +156,14 @@ public SqmJoin createSqmJoin( @Nullable String alias, boolean fetched, SqmCreationState creationState) { - final NodeBuilder nodeBuilder = creationState.getCreationContext().getNodeBuilder(); + final var nodeBuilder = creationState.getCreationContext().getNodeBuilder(); if ( getType() instanceof AnyMappingDomainType ) { throw new SemanticException( "An @Any attribute cannot be join fetched" ); } else if ( sqmPathSource.getPathType() instanceof BasicPluralType ) { final SqmSetReturningFunction setReturningFunction = nodeBuilder.unnestArray( lhs.get( getName() ) ); - //noinspection unchecked - final SqmFunctionJoin join = new SqmFunctionJoin<>( + final var join = new SqmFunctionJoin<>( createNavigablePath( lhs, alias ), setReturningFunction, true, @@ -232,11 +230,11 @@ public NavigablePath createNavigablePath(SqmPath parent, @Nullable String ali "LHS cannot be null for a sub-navigable reference - " + getName() ); } - final SqmPathSource parentPathSource = parent.getResolvedModel(); - final NavigablePath parentNavigablePath = - parentPathSource instanceof PluralPersistentAttribute - ? parent.getNavigablePath().append( CollectionPart.Nature.ELEMENT.getName() ) - : parent.getNavigablePath(); + final var navigablePath = parent.getNavigablePath(); + final var parentNavigablePath = + parent.getResolvedModel() instanceof PluralPersistentAttribute + ? navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ) + : navigablePath; if ( getDeclaringType() instanceof IdentifiableDomainType declaringType && !declaringType.hasSingleIdAttribute() ) { return new EntityIdentifierNavigablePath( parentNavigablePath, null ) @@ -291,7 +289,7 @@ public boolean isOptional() { @Override public boolean isAssociation() { - final PersistentAttributeType persistentAttributeType = getPersistentAttributeType(); + final var persistentAttributeType = getPersistentAttributeType(); return persistentAttributeType == PersistentAttributeType.MANY_TO_ONE || persistentAttributeType == PersistentAttributeType.ONE_TO_ONE; } From fa56009070a73dab2a89a699bc0afa7648475870 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 8 Dec 2025 22:12:00 +0100 Subject: [PATCH 064/181] pull implementations down off SessionFactory --- .../java/org/hibernate/SessionFactory.java | 21 +++++++------------ .../engine/spi/SessionFactoryImplementor.java | 16 ++++++++++++++ 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/SessionFactory.java b/hibernate-core/src/main/java/org/hibernate/SessionFactory.java index a90a43719c6f..184e9da8b18f 100644 --- a/hibernate-core/src/main/java/org/hibernate/SessionFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/SessionFactory.java @@ -13,7 +13,6 @@ import jakarta.persistence.TypedQueryReference; import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.engine.spi.FilterDefinition; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.graph.GraphParser; import org.hibernate.graph.InvalidGraphException; import org.hibernate.graph.RootGraph; @@ -108,7 +107,7 @@ * SingularAttribute<Book,String>}. * *

- * Use of these statically-typed metamodel references is the preferred way of + * Use of these statically typed metamodel references is the preferred way of * working with the {@linkplain jakarta.persistence.criteria.CriteriaBuilder * criteria query API}, and with {@linkplain EntityGraph}s. *

@@ -266,7 +265,7 @@ default void inSession(Consumer action) { * @since 6.3 */ default void inStatelessSession(Consumer action) { - try ( StatelessSession session = openStatelessSession() ) { + try ( var session = openStatelessSession() ) { action.accept( session ); } } @@ -308,7 +307,7 @@ default void inStatelessTransaction(Consumer action) { * @see #fromTransaction(Function) */ default R fromSession(Function action) { - try ( Session session = openSession() ) { + try ( var session = openSession() ) { return action.apply( session ); } } @@ -331,7 +330,7 @@ default R fromSession(Function action) { * @since 6.3 */ default R fromStatelessSession(Function action) { - try ( StatelessSession session = openStatelessSession() ) { + try ( var session = openStatelessSession() ) { return action.apply( session ); } } @@ -522,9 +521,7 @@ default RootGraph createEntityGraph(Class entityType) { * * @since 7.0 */ - default RootGraph parseEntityGraph(Class rootEntityClass, CharSequence graphText) { - return GraphParser.parse( rootEntityClass, graphText.toString(), unwrap( SessionFactoryImplementor.class ) ); - } + RootGraph parseEntityGraph(Class rootEntityClass, CharSequence graphText); /** * Creates a {@link RootGraph} for the given {@code rootEntityName} and parses the graph @@ -544,9 +541,7 @@ default RootGraph parseEntityGraph(Class rootEntityClass, CharSequence * @since 7.0 */ @Incubating - default RootGraph parseEntityGraph(String rootEntityName, CharSequence graphText) { - return GraphParser.parse( rootEntityName, graphText.toString(), unwrap( SessionFactoryImplementor.class ) ); - } + RootGraph parseEntityGraph(String rootEntityName, CharSequence graphText); /** * Creates a {@link RootGraph} based on the passed string representation. Here, the @@ -562,9 +557,7 @@ default RootGraph parseEntityGraph(String rootEntityName, CharSequence gr * @since 7.0 */ @Incubating - default RootGraph parseEntityGraph(CharSequence graphText) { - return GraphParser.parse( graphText.toString(), unwrap( SessionFactoryImplementor.class ) ); - } + RootGraph parseEntityGraph(CharSequence graphText); /** * Obtain the set of names of all {@linkplain org.hibernate.annotations.FilterDef diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java index 84831c176e26..a83bf40786a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java @@ -23,6 +23,8 @@ import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.EntityCopyObserverFactory; import org.hibernate.event.spi.EventEngine; +import org.hibernate.graph.GraphParser; +import org.hibernate.graph.RootGraph; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.event.service.spi.EventListenerGroups; import org.hibernate.metamodel.model.domain.JpaMetamodel; @@ -322,4 +324,18 @@ default JdbcSelectWithActionsBuilder getJdbcSelectWithActionsBuilder(){ return new JdbcSelectWithActions.Builder(); } + @Override + default RootGraph parseEntityGraph(Class rootEntityClass, CharSequence graphText) { + return GraphParser.parse( rootEntityClass, graphText.toString(), unwrap( SessionFactoryImplementor.class ) ); + } + + @Override @Incubating + default RootGraph parseEntityGraph(String rootEntityName, CharSequence graphText) { + return GraphParser.parse( rootEntityName, graphText.toString(), unwrap( SessionFactoryImplementor.class ) ); + } + + @Override @Incubating + default RootGraph parseEntityGraph(CharSequence graphText) { + return GraphParser.parse( graphText.toString(), unwrap( SessionFactoryImplementor.class ) ); + } } From 47d28d17fd60862f0e7bcdadcdd148f4d3680054 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 8 Dec 2025 22:20:55 +0100 Subject: [PATCH 065/181] get rid of unnecessary EntityNameResolverSessionFactory + extract methods --- .../internal/NamedGraphCreatorParsed.java | 40 +++++++++++-------- .../EntityNameResolverSessionFactory.java | 24 ----------- .../graph/internal/parse/GraphParser.java | 2 +- 3 files changed, 24 insertions(+), 42 deletions(-) delete mode 100644 hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolverSessionFactory.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java index f0646c26568f..897cb909e859 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java @@ -6,6 +6,7 @@ import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.UnknownEntityTypeException; import org.hibernate.annotations.NamedEntityGraph; @@ -13,7 +14,6 @@ import org.hibernate.grammars.graph.GraphLanguageLexer; import org.hibernate.grammars.graph.GraphLanguageParser; import org.hibernate.graph.InvalidGraphException; -import org.hibernate.graph.internal.parse.EntityNameResolver; import org.hibernate.graph.internal.parse.GraphParsing; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.metamodel.model.domain.EntityDomainType; @@ -48,33 +48,39 @@ public RootGraphImplementor createEntityGraph( final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); final var graphContext = parser.graph(); - final var entityNameResolver = new EntityNameResolver() { - @Override - public EntityDomainType resolveEntityName(String entityName) { - final var entityDomainType = (EntityDomainType) entityDomainNameResolver.apply( entityName ); - if ( entityDomainType != null ) { - return entityDomainType; - } - throw new UnknownEntityTypeException( entityName ); - } - }; - + final var typeIndicator = graphContext.typeIndicator(); if ( entityType == null ) { - if ( graphContext.typeIndicator() == null ) { + if ( typeIndicator == null ) { throw new InvalidGraphException( "Expecting graph text to include an entity name : " + annotation.graph() ); } - final String jpaEntityName = graphContext.typeIndicator().TYPE_NAME().toString(); + final String jpaEntityName = typeIndicator.TYPE_NAME().toString(); final var entityDomainType = entityDomainNameResolver.apply( jpaEntityName ); final String name = this.name == null ? jpaEntityName : this.name; - return GraphParsing.parse( name, entityDomainType, graphContext.attributeList(), entityNameResolver ); + return parse( entityDomainNameResolver, name, entityDomainType, graphContext ); } else { - if ( graphContext.typeIndicator() != null ) { + if ( typeIndicator != null ) { throw new InvalidGraphException( "Expecting graph text to not include an entity name : " + annotation.graph() ); } final var entityDomainType = entityDomainClassResolver.apply( entityType ); final String name = this.name == null ? entityDomainType.getName() : this.name; - return GraphParsing.parse( name, entityDomainType, graphContext.attributeList(), entityNameResolver ); + return parse( entityDomainNameResolver, name, entityDomainType, graphContext ); + } + } + + private static @NonNull RootGraphImplementor parse( + Function> entityDomainNameResolver, String name, EntityDomainType entityDomainType, + GraphLanguageParser.GraphContext graphContext) { + return GraphParsing.parse( name, entityDomainType, graphContext.attributeList(), + entityName -> resolve( entityName, entityDomainNameResolver ) ); + } + + private static @NonNull EntityDomainType resolve( + String entityName, Function> entityDomainNameResolver) { + final var entityDomainType = (EntityDomainType) entityDomainNameResolver.apply( entityName ); + if ( entityDomainType != null ) { + return entityDomainType; } + throw new UnknownEntityTypeException( entityName ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolverSessionFactory.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolverSessionFactory.java deleted file mode 100644 index 1a7e8a43c074..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolverSessionFactory.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.graph.internal.parse; - -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.metamodel.model.domain.EntityDomainType; - -/** - * @author Steve Ebersole - */ -public class EntityNameResolverSessionFactory implements EntityNameResolver { - private final SessionFactoryImplementor sessionFactory; - - public EntityNameResolverSessionFactory(SessionFactoryImplementor sessionFactory) { - this.sessionFactory = sessionFactory; - } - - @Override - public EntityDomainType resolveEntityName(String entityName) { - return sessionFactory.getJpaMetamodel().findEntityType( entityName ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java index 7953808c610f..885f54d8e867 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java @@ -41,7 +41,7 @@ public GraphParser(EntityNameResolver entityNameResolver) { * @see GraphParser#GraphParser(EntityNameResolver) */ public GraphParser(SessionFactoryImplementor sessionFactory) { - this( new EntityNameResolverSessionFactory( sessionFactory ) ); + this( sessionFactory.getJpaMetamodel()::findEntityType ); } public Stack> getGraphStack() { From b06948373a586666010db58a9ca2979703a02e27 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 8 Dec 2025 22:41:03 +0100 Subject: [PATCH 066/181] extract duplicated code in GraphParsing --- .../graph/internal/parse/GraphParsing.java | 97 ++++++------------- 1 file changed, 32 insertions(+), 65 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java index d1d1ecb77ae2..8f77dc25ea74 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java @@ -6,10 +6,12 @@ import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.grammars.graph.GraphLanguageLexer; import org.hibernate.grammars.graph.GraphLanguageParser; +import org.hibernate.grammars.graph.GraphLanguageParser.GraphContext; import org.hibernate.graph.InvalidGraphException; import org.hibernate.graph.internal.RootGraphImpl; import org.hibernate.graph.spi.GraphImplementor; @@ -22,69 +24,39 @@ * @author Steve Ebersole */ public class GraphParsing { + public static RootGraphImplementor parse( - Class entityClass, + EntityDomainType entityDomainType, String graphText, SessionFactoryImplementor sessionFactory) { if ( graphText == null ) { return null; } - final var lexer = new GraphLanguageLexer( CharStreams.fromString( graphText ) ); - final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); - final var graphContext = parser.graph(); - + final var graphContext = parseText( graphText ); if ( graphContext.typeIndicator() != null ) { // todo : an alternative here would be to simply validate that the entity type // from the text matches the passed one... - throw new InvalidGraphException( "Expecting graph text to not include an entity name : " + graphText ); + throw new InvalidGraphException( "Expecting graph text to not include an entity name: " + graphText ); } - final var entityType = sessionFactory.getJpaMetamodel().entity( entityClass ); - return parse( entityType, graphContext.attributeList(), sessionFactory ); + return parse( entityDomainType, graphContext.attributeList(), sessionFactory ); } public static RootGraphImplementor parse( - EntityDomainType entityDomainType, + Class entityClass, String graphText, SessionFactoryImplementor sessionFactory) { - if ( graphText == null ) { - return null; - } - - final var lexer = new GraphLanguageLexer( CharStreams.fromString( graphText ) ); - final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); - final var graphContext = parser.graph(); - - if ( graphContext.typeIndicator() != null ) { - // todo : an alternative here would be to simply validate that the entity type - // from the text matches the passed one... - throw new InvalidGraphException( "Expecting graph text to not include an entity name : " + graphText ); - } - - return parse( entityDomainType, graphContext.attributeList(), sessionFactory ); + return parse( sessionFactory.getJpaMetamodel().entity( entityClass ), + graphText, sessionFactory ); } public static RootGraphImplementor parse( String entityName, String graphText, SessionFactoryImplementor sessionFactory) { - if ( graphText == null ) { - return null; - } - - final var lexer = new GraphLanguageLexer( CharStreams.fromString( graphText ) ); - final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); - final var graphContext = parser.graph(); - - if ( graphContext.typeIndicator() != null ) { - // todo : an alternative here would be to simply validate that the entity type - // from the text matches the passed one... - throw new InvalidGraphException( "Expecting graph text to not include an entity name : " + graphText ); - } - - final var entityType = sessionFactory.getJpaMetamodel().entity( entityName ); - return parse( entityType, graphContext.attributeList(), sessionFactory ); + return parse( sessionFactory.getJpaMetamodel().entity( entityName ), + graphText, sessionFactory ); } public static RootGraphImplementor parse( @@ -94,12 +66,9 @@ public static RootGraphImplementor parse( return null; } - final var lexer = new GraphLanguageLexer( CharStreams.fromString( graphText ) ); - final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); - final var graphContext = parser.graph(); - + final var graphContext = parseText( graphText ); if ( graphContext.typeIndicator() == null ) { - throw new InvalidGraphException( "Expecting graph text to include an entity name : " + graphText ); + throw new InvalidGraphException( "Expecting graph text to include an entity name: " + graphText ); } final String entityName = graphContext.typeIndicator().TYPE_NAME().getText(); @@ -121,24 +90,19 @@ public static RootGraphImplementor parse( return parse( null, rootType, attributeListContext, entityNameResolver ); } + private static @NonNull GraphContext parseText(String graphText) { + final var lexer = new GraphLanguageLexer( CharStreams.fromString( graphText ) ); + final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); + return parser.graph(); + } + public static RootGraphImplementor parse( @Nullable String name, EntityDomainType rootType, GraphLanguageParser.AttributeListContext attributeListContext, EntityNameResolver entityNameResolver) { final RootGraphImpl targetGraph = new RootGraphImpl<>( name, rootType ); - - final var visitor = new GraphParser( entityNameResolver ); - visitor.getGraphStack().push( targetGraph ); - try { - visitor.visitAttributeList( attributeListContext ); - } - finally { - visitor.getGraphStack().pop(); - - assert visitor.getGraphStack().isEmpty(); - } - + visitGraph( targetGraph, entityNameResolver, attributeListContext ); return targetGraph; } @@ -150,25 +114,28 @@ public static void parseInto( GraphImplementor targetGraph, CharSequence graphString, SessionFactoryImplementor sessionFactory) { - final var lexer = new GraphLanguageLexer( CharStreams.fromString( graphString.toString() ) ); - final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); - final var graphContext = parser.graph(); - + final var graphContext = parseText( graphString.toString() ); if ( graphContext.typeIndicator() != null ) { // todo : throw an exception? Log warning? Ignore? // for now, ignore } + visitGraph( targetGraph, + sessionFactory.getJpaMetamodel()::findEntityType, + graphContext.attributeList() ); + } + private static void visitGraph( + GraphImplementor targetGraph, + EntityNameResolver entityNameResolver, + GraphLanguageParser.AttributeListContext attributeList) { // Build an instance of this class as a visitor - final var visitor = new GraphParser( sessionFactory ); - + final var visitor = new GraphParser( entityNameResolver ); visitor.getGraphStack().push( targetGraph ); try { - visitor.visitAttributeList( graphContext.attributeList() ); + visitor.visitAttributeList( attributeList ); } finally { visitor.getGraphStack().pop(); - assert visitor.getGraphStack().isEmpty(); } } From 7f3424174f735fb109f2c3c0aeb354c27b5ff256 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 8 Dec 2025 23:18:42 +0100 Subject: [PATCH 067/181] factor out GraphParserEntityXXXXXResolver interfaces --- .../boot/model/NamedGraphCreator.java | 9 +++--- .../model/internal/NamedGraphCreatorJpa.java | 10 +++---- .../internal/NamedGraphCreatorParsed.java | 28 +++++++++++-------- .../java/org/hibernate/graph/GraphParser.java | 2 -- .../graph/internal/parse/GraphParser.java | 7 +++-- .../graph/internal/parse/GraphParsing.java | 7 +++-- .../internal/parse/PathQualifierType.java | 5 ++-- .../internal/parse/SubGraphGenerator.java | 3 +- .../spi/GraphParserEntityClassResolver.java | 19 +++++++++++++ .../GraphParserEntityNameResolver.java} | 8 ++++-- .../domain/internal/JpaMetamodelImpl.java | 18 ++++++------ 11 files changed, 73 insertions(+), 43 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/graph/spi/GraphParserEntityClassResolver.java rename hibernate-core/src/main/java/org/hibernate/graph/{internal/parse/EntityNameResolver.java => spi/GraphParserEntityNameResolver.java} (65%) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/NamedGraphCreator.java b/hibernate-core/src/main/java/org/hibernate/boot/model/NamedGraphCreator.java index 45e271aec6dd..098fbbe844e2 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/NamedGraphCreator.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/NamedGraphCreator.java @@ -4,10 +4,9 @@ */ package org.hibernate.boot.model; +import org.hibernate.graph.spi.GraphParserEntityClassResolver; +import org.hibernate.graph.spi.GraphParserEntityNameResolver; import org.hibernate.graph.spi.RootGraphImplementor; -import org.hibernate.metamodel.model.domain.EntityDomainType; - -import java.util.function.Function; /** * @author Steve Ebersole @@ -15,6 +14,6 @@ @FunctionalInterface public interface NamedGraphCreator { RootGraphImplementor createEntityGraph( - Function, EntityDomainType> entityDomainClassResolver, - Function> entityDomainNameResolver); + GraphParserEntityClassResolver entityDomainClassResolver, + GraphParserEntityNameResolver entityDomainNameResolver); } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorJpa.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorJpa.java index 7321a8b65ef0..852df030cc65 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorJpa.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorJpa.java @@ -11,13 +11,13 @@ import org.hibernate.boot.model.NamedGraphCreator; import org.hibernate.graph.internal.RootGraphImpl; import org.hibernate.graph.spi.AttributeNodeImplementor; +import org.hibernate.graph.spi.GraphParserEntityClassResolver; +import org.hibernate.graph.spi.GraphParserEntityNameResolver; import org.hibernate.graph.spi.GraphImplementor; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.graph.spi.SubGraphImplementor; import org.hibernate.metamodel.model.domain.EntityDomainType; -import java.util.function.Function; - import static org.hibernate.internal.util.StringHelper.isNotEmpty; import static org.hibernate.internal.util.StringHelper.nullIfEmpty; @@ -38,10 +38,10 @@ class NamedGraphCreatorJpa implements NamedGraphCreator { @Override public RootGraphImplementor createEntityGraph( - Function, EntityDomainType> entityDomainClassResolver, - Function> entityDomainNameResolver) { + GraphParserEntityClassResolver entityDomainClassResolver, + GraphParserEntityNameResolver entityDomainNameResolver) { return createGraph( (EntityDomainType) - entityDomainNameResolver.apply( jpaEntityName ) ); + entityDomainNameResolver.resolveEntityName( jpaEntityName ) ); } private @NonNull RootGraphImplementor createGraph(EntityDomainType rootEntityType) { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java index 897cb909e859..ed0ca3054e50 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java @@ -14,12 +14,12 @@ import org.hibernate.grammars.graph.GraphLanguageLexer; import org.hibernate.grammars.graph.GraphLanguageParser; import org.hibernate.graph.InvalidGraphException; +import org.hibernate.graph.spi.GraphParserEntityClassResolver; +import org.hibernate.graph.spi.GraphParserEntityNameResolver; import org.hibernate.graph.internal.parse.GraphParsing; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.metamodel.model.domain.EntityDomainType; -import java.util.function.Function; - import static org.hibernate.internal.util.StringHelper.nullIfEmpty; /** @@ -42,8 +42,8 @@ class NamedGraphCreatorParsed implements NamedGraphCreator { @Override public RootGraphImplementor createEntityGraph( - Function, EntityDomainType> entityDomainClassResolver, - Function> entityDomainNameResolver) { + GraphParserEntityClassResolver entityDomainClassResolver, + GraphParserEntityNameResolver entityDomainNameResolver) { final var lexer = new GraphLanguageLexer( CharStreams.fromString( annotation.graph() ) ); final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); final var graphContext = parser.graph(); @@ -54,7 +54,7 @@ public RootGraphImplementor createEntityGraph( throw new InvalidGraphException( "Expecting graph text to include an entity name : " + annotation.graph() ); } final String jpaEntityName = typeIndicator.TYPE_NAME().toString(); - final var entityDomainType = entityDomainNameResolver.apply( jpaEntityName ); + final var entityDomainType = entityDomainNameResolver.resolveEntityName( jpaEntityName ); final String name = this.name == null ? jpaEntityName : this.name; return parse( entityDomainNameResolver, name, entityDomainType, graphContext ); } @@ -62,25 +62,31 @@ public RootGraphImplementor createEntityGraph( if ( typeIndicator != null ) { throw new InvalidGraphException( "Expecting graph text to not include an entity name : " + annotation.graph() ); } - final var entityDomainType = entityDomainClassResolver.apply( entityType ); + final var entityDomainType = entityDomainClassResolver.resolveEntityClass( entityType ); final String name = this.name == null ? entityDomainType.getName() : this.name; return parse( entityDomainNameResolver, name, entityDomainType, graphContext ); } } private static @NonNull RootGraphImplementor parse( - Function> entityDomainNameResolver, String name, EntityDomainType entityDomainType, + GraphParserEntityNameResolver entityDomainNameResolver, + String name, + EntityDomainType entityDomainType, GraphLanguageParser.GraphContext graphContext) { return GraphParsing.parse( name, entityDomainType, graphContext.attributeList(), entityName -> resolve( entityName, entityDomainNameResolver ) ); } private static @NonNull EntityDomainType resolve( - String entityName, Function> entityDomainNameResolver) { - final var entityDomainType = (EntityDomainType) entityDomainNameResolver.apply( entityName ); - if ( entityDomainType != null ) { + String entityName, GraphParserEntityNameResolver entityDomainNameResolver) { + final var entityDomainType = + (EntityDomainType) + entityDomainNameResolver.resolveEntityName( entityName ); + if ( entityDomainType == null ) { + throw new UnknownEntityTypeException( entityName ); + } + else { return entityDomainType; } - throw new UnknownEntityTypeException( entityName ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/GraphParser.java b/hibernate-core/src/main/java/org/hibernate/graph/GraphParser.java index 690246372dea..ae5ca1086929 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/GraphParser.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/GraphParser.java @@ -291,8 +291,6 @@ public static void parseInto( * Parses the textual graph representation as {@linkplain GraphParser described above} * into the specified graph. * - * @param The Java type for the ManagedType described by `graph` - * * @param graph The target graph. This is the graph that will be populated * by this process * @param graphText Textual representation of the graph diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java index 885f54d8e867..1377b8678ac1 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java @@ -10,6 +10,7 @@ import org.hibernate.graph.GraphNode; import org.hibernate.graph.InvalidGraphException; import org.hibernate.graph.spi.AttributeNodeImplementor; +import org.hibernate.graph.spi.GraphParserEntityNameResolver; import org.hibernate.graph.spi.GraphImplementor; import org.hibernate.graph.spi.SubGraphImplementor; import org.hibernate.internal.util.StringHelper; @@ -24,13 +25,13 @@ * @author Steve Ebersole */ public class GraphParser extends GraphLanguageParserBaseVisitor> { - private final EntityNameResolver entityNameResolver; + private final GraphParserEntityNameResolver entityNameResolver; private final Stack> graphStack = new StandardStack<>(); private final Stack> attributeNodeStack = new StandardStack<>(); private final Stack graphSourceStack = new StandardStack<>(); - public GraphParser(EntityNameResolver entityNameResolver) { + public GraphParser(GraphParserEntityNameResolver entityNameResolver) { this.entityNameResolver = entityNameResolver; } @@ -38,7 +39,7 @@ public GraphParser(EntityNameResolver entityNameResolver) { * @apiNote It is important that this form only be used after the session-factory is fully * initialized, especially the {@linkplain SessionFactoryImplementor#getJpaMetamodel()} JPA metamodel}. * - * @see GraphParser#GraphParser(EntityNameResolver) + * @see GraphParser#GraphParser(GraphParserEntityNameResolver) */ public GraphParser(SessionFactoryImplementor sessionFactory) { this( sessionFactory.getJpaMetamodel()::findEntityType ); diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java index 8f77dc25ea74..332ce3885fef 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java @@ -14,6 +14,7 @@ import org.hibernate.grammars.graph.GraphLanguageParser.GraphContext; import org.hibernate.graph.InvalidGraphException; import org.hibernate.graph.internal.RootGraphImpl; +import org.hibernate.graph.spi.GraphParserEntityNameResolver; import org.hibernate.graph.spi.GraphImplementor; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.metamodel.model.domain.EntityDomainType; @@ -86,7 +87,7 @@ public static RootGraphImplementor parse( public static RootGraphImplementor parse( EntityDomainType rootType, GraphLanguageParser.AttributeListContext attributeListContext, - EntityNameResolver entityNameResolver) { + GraphParserEntityNameResolver entityNameResolver) { return parse( null, rootType, attributeListContext, entityNameResolver ); } @@ -100,7 +101,7 @@ public static RootGraphImplementor parse( @Nullable String name, EntityDomainType rootType, GraphLanguageParser.AttributeListContext attributeListContext, - EntityNameResolver entityNameResolver) { + GraphParserEntityNameResolver entityNameResolver) { final RootGraphImpl targetGraph = new RootGraphImpl<>( name, rootType ); visitGraph( targetGraph, entityNameResolver, attributeListContext ); return targetGraph; @@ -126,7 +127,7 @@ public static void parseInto( private static void visitGraph( GraphImplementor targetGraph, - EntityNameResolver entityNameResolver, + GraphParserEntityNameResolver entityNameResolver, GraphLanguageParser.AttributeListContext attributeList) { // Build an instance of this class as a visitor final var visitor = new GraphParser( entityNameResolver ); diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/PathQualifierType.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/PathQualifierType.java index 24a5fa062ecb..f9973dcf3e49 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/PathQualifierType.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/PathQualifierType.java @@ -5,6 +5,7 @@ package org.hibernate.graph.internal.parse; +import org.hibernate.graph.spi.GraphParserEntityNameResolver; import org.hibernate.metamodel.model.domain.ManagedDomainType; /** @@ -22,8 +23,8 @@ public enum PathQualifierType { : attributeNode.addValueSubgraph().addTreatedSubgraph( managedType( subtypeName, entityNameResolver ) ) ); - private static ManagedDomainType managedType(String subtypeName, EntityNameResolver entityNameResolver) { - final var entityDomainType = entityNameResolver.resolveEntityName( subtypeName ); + private static ManagedDomainType managedType(String subtypeName, GraphParserEntityNameResolver resolver) { + final var entityDomainType = resolver.resolveEntityName( subtypeName ); if ( entityDomainType == null ) { throw new IllegalArgumentException( "Unknown managed type: " + subtypeName ); } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/SubGraphGenerator.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/SubGraphGenerator.java index f575b2b9a624..295c33d93f64 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/SubGraphGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/SubGraphGenerator.java @@ -5,6 +5,7 @@ package org.hibernate.graph.internal.parse; import org.hibernate.graph.spi.AttributeNodeImplementor; +import org.hibernate.graph.spi.GraphParserEntityNameResolver; import org.hibernate.graph.spi.SubGraphImplementor; /** @@ -15,5 +16,5 @@ public interface SubGraphGenerator { SubGraphImplementor createSubGraph( AttributeNodeImplementor attributeNode, String subTypeName, - EntityNameResolver entityNameResolver); + GraphParserEntityNameResolver entityNameResolver); } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/spi/GraphParserEntityClassResolver.java b/hibernate-core/src/main/java/org/hibernate/graph/spi/GraphParserEntityClassResolver.java new file mode 100644 index 000000000000..7210cddaae5f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/graph/spi/GraphParserEntityClassResolver.java @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.graph.spi; + +import org.hibernate.Incubating; +import org.hibernate.metamodel.model.domain.EntityDomainType; + +/** + * @author Gavin King + * + * @since 7.2 + */ +@Incubating +@FunctionalInterface +public interface GraphParserEntityClassResolver { + EntityDomainType resolveEntityClass(Class entityClass); +} diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolver.java b/hibernate-core/src/main/java/org/hibernate/graph/spi/GraphParserEntityNameResolver.java similarity index 65% rename from hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolver.java rename to hibernate-core/src/main/java/org/hibernate/graph/spi/GraphParserEntityNameResolver.java index d1bfce497a59..3734bd8b4086 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/EntityNameResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/spi/GraphParserEntityNameResolver.java @@ -2,14 +2,18 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.graph.internal.parse; +package org.hibernate.graph.spi; +import org.hibernate.Incubating; import org.hibernate.metamodel.model.domain.EntityDomainType; /** * @author Steve Ebersole + * + * @since 7.2 */ +@Incubating @FunctionalInterface -public interface EntityNameResolver { +public interface GraphParserEntityNameResolver { EntityDomainType resolveEntityName(String entityName); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java index ef0b6eafad14..36a76ab9f4cc 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java @@ -451,7 +451,7 @@ private ImportInfo resolveImport(final String name) { return importInfo; } else { - //then check the negative cache, to avoid bothering the classloader unnecessarily + //then check the negative cache to avoid bothering the classloader unnecessarily if ( knownInvalidnameToImportMap.containsKey( name ) ) { return null; } @@ -491,13 +491,13 @@ private void applyNamedEntityGraphs(Collection named definition.name(), definition.source() ); final var graph = definition.graphCreator().createEntityGraph( - (entityClass) -> { + entityClass -> { if ( managedTypeByClass.get( entityClass ) instanceof EntityDomainType match ) { return match; } throw new IllegalArgumentException( "Cannot resolve entity class : " + entityClass.getName() ); }, - (jpaEntityName) -> { + jpaEntityName -> { for ( var entry : managedTypeByName.entrySet() ) { if ( entry.getValue() instanceof EntityDomainType possibility && jpaEntityName.equals( possibility.getName() ) ) { @@ -554,14 +554,14 @@ public EntityDomainType resolveEntityReference(Class javaType) { if ( managedType.getPersistenceType() == Type.PersistenceType.ENTITY // see if we should add EntityDomainType as one of the matching descriptors. && javaType.isAssignableFrom( managedType.getJavaType() ) ) { - // the queried type is assignable from the type of the current entity-type - // we should add it to the collecting set of matching descriptors. it should + // The queried type is assignable from the type of the current entity type. + // We should add it to the collecting set of matching descriptors. It should // be added aside from a few cases... - // if the managed-type has a super type and the java type is assignable from the super type, - // do not add the managed type as the super itself will get added and the initializers for - // entity mappings already handle loading subtypes - adding it would be redundant and lead to - // incorrect results + // If the managed type has a supertype and the java type is assignable from the super type, + // do not add the managed type as the supertype itself will get added and the initializers + // for entity mappings already handle loading subtypes - adding it would be redundant and + // lead to incorrect results final var superType = managedType.getSuperType(); if ( superType == null || superType.getPersistenceType() != Type.PersistenceType.ENTITY From 4c06810dfdf3331969a2e6ae039bfe78235724fa Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 9 Dec 2025 10:32:24 +0100 Subject: [PATCH 068/181] some refactoring and cleanups in the graph package --- .../annotations/NamedEntityGraph.java | 2 +- .../internal/NamedGraphCreatorParsed.java | 35 +++--- .../org/hibernate/graph/EntityGraphs.java | 111 +++++++----------- .../java/org/hibernate/graph/GraphParser.java | 8 +- .../graph/internal/parse/GraphParser.java | 67 +++++------ .../graph/internal/parse/GraphParsing.java | 16 +-- 6 files changed, 102 insertions(+), 137 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/NamedEntityGraph.java b/hibernate-core/src/main/java/org/hibernate/annotations/NamedEntityGraph.java index aa5aed0ac291..e3b397ff475d 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/NamedEntityGraph.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/NamedEntityGraph.java @@ -45,7 +45,7 @@ * Entity graph names must be unique within the persistence unit. *

* When applied to a root entity class, the name is optional and - * defaults to the entity-name of that entity. + * defaults to the JPA entity name of that entity. */ String name() default ""; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java index ed0ca3054e50..01b29907259b 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/NamedGraphCreatorParsed.java @@ -4,14 +4,11 @@ */ package org.hibernate.boot.model.internal; -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.CommonTokenStream; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.UnknownEntityTypeException; import org.hibernate.annotations.NamedEntityGraph; import org.hibernate.boot.model.NamedGraphCreator; -import org.hibernate.grammars.graph.GraphLanguageLexer; import org.hibernate.grammars.graph.GraphLanguageParser; import org.hibernate.graph.InvalidGraphException; import org.hibernate.graph.spi.GraphParserEntityClassResolver; @@ -20,6 +17,7 @@ import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.metamodel.model.domain.EntityDomainType; +import static org.hibernate.graph.internal.parse.GraphParsing.parseText; import static org.hibernate.internal.util.StringHelper.nullIfEmpty; /** @@ -44,36 +42,33 @@ class NamedGraphCreatorParsed implements NamedGraphCreator { public RootGraphImplementor createEntityGraph( GraphParserEntityClassResolver entityDomainClassResolver, GraphParserEntityNameResolver entityDomainNameResolver) { - final var lexer = new GraphLanguageLexer( CharStreams.fromString( annotation.graph() ) ); - final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); - final var graphContext = parser.graph(); - + final var graphContext = parseText( annotation.graph() ); final var typeIndicator = graphContext.typeIndicator(); + final EntityDomainType entityDomainType; + final String jpaEntityName; if ( entityType == null ) { if ( typeIndicator == null ) { - throw new InvalidGraphException( "Expecting graph text to include an entity name : " + annotation.graph() ); + throw new InvalidGraphException( "Expecting graph text to include an entity name: " + annotation.graph() ); } - final String jpaEntityName = typeIndicator.TYPE_NAME().toString(); - final var entityDomainType = entityDomainNameResolver.resolveEntityName( jpaEntityName ); - final String name = this.name == null ? jpaEntityName : this.name; - return parse( entityDomainNameResolver, name, entityDomainType, graphContext ); + jpaEntityName = typeIndicator.TYPE_NAME().toString(); + entityDomainType = entityDomainNameResolver.resolveEntityName( jpaEntityName ); } else { if ( typeIndicator != null ) { - throw new InvalidGraphException( "Expecting graph text to not include an entity name : " + annotation.graph() ); + throw new InvalidGraphException( "Expecting graph text to not include an entity name: " + annotation.graph() ); } - final var entityDomainType = entityDomainClassResolver.resolveEntityClass( entityType ); - final String name = this.name == null ? entityDomainType.getName() : this.name; - return parse( entityDomainNameResolver, name, entityDomainType, graphContext ); + entityDomainType = entityDomainClassResolver.resolveEntityClass( entityType ); + jpaEntityName = entityDomainType.getName(); } + return visit( name == null ? jpaEntityName : name, + entityDomainType, entityDomainNameResolver, graphContext ); } - private static @NonNull RootGraphImplementor parse( - GraphParserEntityNameResolver entityDomainNameResolver, + private static @NonNull RootGraphImplementor visit( String name, - EntityDomainType entityDomainType, + EntityDomainType entityDomainType, GraphParserEntityNameResolver entityDomainNameResolver, GraphLanguageParser.GraphContext graphContext) { - return GraphParsing.parse( name, entityDomainType, graphContext.attributeList(), + return GraphParsing.visit( name, entityDomainType, graphContext.attributeList(), entityName -> resolve( entityName, entityDomainNameResolver ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/EntityGraphs.java b/hibernate-core/src/main/java/org/hibernate/graph/EntityGraphs.java index 9106be1fd9d9..9d44ffbd78c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/EntityGraphs.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/EntityGraphs.java @@ -7,7 +7,6 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.stream.Stream; import jakarta.persistence.AttributeNode; @@ -310,31 +309,10 @@ public static boolean areEqual(EntityGraph a, EntityGraph b) { if ( a == b ) { return true; } - if ( ( a == null ) || ( b == null ) ) { - return false; - } - - final List> aNodes = a.getAttributeNodes(); - final List> bNodes = b.getAttributeNodes(); - - if ( aNodes.size() != bNodes.size() ) { - return false; - } - for ( AttributeNode aNode : aNodes ) { - final String attributeName = aNode.getAttributeName(); - AttributeNode bNode = null; - for ( AttributeNode bCandidate : bNodes ) { - if ( attributeName.equals( bCandidate.getAttributeName() ) ) { - bNode = bCandidate; - break; - } - } - if ( !areEqual( aNode, bNode ) ) { - return false; - } + else { + return a != null && b != null + && haveSameNodes( a, b ); } - - return true; } /** @@ -345,10 +323,10 @@ public static boolean areEqual(AttributeNode a, AttributeNode b) { if ( a == b ) { return true; } - if ( ( a == null ) || ( b == null ) ) { + else if ( a == null || b == null ) { return false; } - if ( a.getAttributeName().equals( b.getAttributeName() ) ) { + else if ( a.getAttributeName().equals( b.getAttributeName() ) ) { return areEqual( a.getSubgraphs(), b.getSubgraphs() ) && areEqual( a.getKeySubgraphs(), b.getKeySubgraphs() ); } @@ -367,28 +345,23 @@ public static boolean areEqual( if ( a == b ) { return true; } - if ( ( a == null ) || ( b == null ) ) { + else if ( a == null || b == null ) { return false; } - - @SuppressWarnings("rawtypes") - final Set aKeys = a.keySet(); - @SuppressWarnings("rawtypes") - final Set bKeys = b.keySet(); - - if ( aKeys.equals( bKeys ) ) { - for ( Class clazz : aKeys ) { - if ( !bKeys.contains( clazz ) ) { - return false; - } - if ( !areEqual( a.get( clazz ), b.get( clazz ) ) ) { - return false; + else { + final var aKeys = a.keySet(); + final var bKeys = b.keySet(); + if ( aKeys.equals( bKeys ) ) { + for ( var key : aKeys ) { + if ( !areEqual( a.get( key ), b.get( key ) ) ) { + return false; + } } + return true; + } + else { + return false; } - return true; - } - else { - return false; } } @@ -396,42 +369,38 @@ public static boolean areEqual( * Compares two entity subgraphs and returns {@code true} if they are equal, * ignoring attribute order. */ - public static boolean areEqual( - @SuppressWarnings("rawtypes") Subgraph a, - @SuppressWarnings("rawtypes") Subgraph b) { + public static boolean areEqual(Subgraph a, Subgraph b) { if ( a == b ) { return true; } - if ( ( a == null ) || ( b == null ) ) { - return false; - } - if ( a.getClassType() != b.getClassType() ) { - return false; + else { + return a != null && b != null + && a.getClassType() == b.getClassType() + && haveSameNodes( a, b ); } + } - @SuppressWarnings("unchecked") - final List> aNodes = a.getAttributeNodes(); - @SuppressWarnings("unchecked") - final List> bNodes = b.getAttributeNodes(); - + private static boolean haveSameNodes(Graph a, Graph b) { + final var aNodes = a.getAttributeNodes(); + final var bNodes = b.getAttributeNodes(); if ( aNodes.size() != bNodes.size() ) { return false; } - - for ( AttributeNode aNode : aNodes ) { - final String attributeName = aNode.getAttributeName(); - AttributeNode bNode = null; - for ( AttributeNode bCandidate : bNodes ) { - if ( attributeName.equals( bCandidate.getAttributeName() ) ) { - bNode = bCandidate; - break; + else { + for ( var aNode : aNodes ) { + final String attributeName = aNode.getAttributeName(); + AttributeNode bNode = null; + for ( var bCandidate : bNodes ) { + if ( attributeName.equals( bCandidate.getAttributeName() ) ) { + bNode = bCandidate; + break; + } + } + if ( !areEqual( aNode, bNode ) ) { + return false; } } - if ( !areEqual( aNode, bNode ) ) { - return false; - } + return true; } - - return true; } } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/GraphParser.java b/hibernate-core/src/main/java/org/hibernate/graph/GraphParser.java index ae5ca1086929..f2386b680453 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/GraphParser.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/GraphParser.java @@ -28,7 +28,8 @@ * The {@link #parse} methods all create a root {@link jakarta.persistence.EntityGraph} * based on the passed entity class and parse the graph string into that root graph. *

- * The {@link #parseInto} methods parse the graph string into a passed graph, which may be a subgraph + * The {@link #parseInto} methods parse the graph string into a passed graph, which may + * be a subgraph. *

* Multiple graphs for the same entity type can be * {@linkplain EntityGraphs#merge(EntityManager, Class, jakarta.persistence.Graph...) @@ -36,7 +37,6 @@ * * @author asusnjar */ -@SuppressWarnings("unused") public final class GraphParser { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -135,8 +135,8 @@ public static RootGraph parse( } /** - * Creates a root graph based on the passed `rootType` and parses `graphText` into - * the generated root graph + * Creates a root graph based on the passed {@code rootType} and parses {@code graphText} + * into the generated root graph. * * @apiNote The passed EntityManager is expected to be a Hibernate implementation. * Attempting to pass another provider's EntityManager implementation will fail. diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java index 1377b8678ac1..2f69953025c9 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java @@ -13,11 +13,11 @@ import org.hibernate.graph.spi.GraphParserEntityNameResolver; import org.hibernate.graph.spi.GraphImplementor; import org.hibernate.graph.spi.SubGraphImplementor; -import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.Stack; import org.hibernate.internal.util.collections.StandardStack; import static org.hibernate.graph.internal.GraphParserLogging.PARSING_LOGGER; +import static org.hibernate.internal.util.StringHelper.repeat; /** * Unified access to the Antlr parser for Hibernate's "graph language" @@ -51,36 +51,12 @@ public Stack> getGraphStack() { @Override public AttributeNodeImplementor visitAttributeNode(GraphLanguageParser.AttributeNodeContext attributeNodeContext) { - final String attributeName = attributeNodeContext.attributePath().ATTR_NAME().getText(); + final var attributePathContext = attributeNodeContext.attributePath(); + final var attributeQualifierContext = attributePathContext.attributeQualifier(); - final SubGraphGenerator subGraphCreator; + final String attributeName = attributePathContext.ATTR_NAME().getText(); - if ( attributeNodeContext.attributePath().attributeQualifier() == null ) { - if ( PARSING_LOGGER.isTraceEnabled() ) { - PARSING_LOGGER.tracef( - "%s Start attribute : %s", - StringHelper.repeat( ">>", attributeNodeStack.depth() + 1 ), - attributeName - ); - } - - subGraphCreator = PathQualifierType.VALUE.getSubGraphCreator(); - } - else { - final String qualifierName = attributeNodeContext.attributePath().attributeQualifier().ATTR_NAME().getText(); - - if ( PARSING_LOGGER.isTraceEnabled() ) { - PARSING_LOGGER.tracef( - "%s Start qualified attribute : %s.%s", - StringHelper.repeat( ">>", attributeNodeStack.depth() + 1 ), - attributeName, - qualifierName - ); - } - - final PathQualifierType pathQualifierType = resolvePathQualifier( qualifierName ); - subGraphCreator = pathQualifierType.getSubGraphCreator(); - } + final var subGraphCreator = subGraphCreator( attributeQualifierContext, attributeName ); final var attributeNode = resolveAttributeNode( attributeName ); @@ -101,7 +77,7 @@ public AttributeNodeImplementor visitAttributeNode(GraphLanguageParser.At if ( PARSING_LOGGER.isTraceEnabled() ) { PARSING_LOGGER.tracef( "%s Finished attribute : %s", - StringHelper.repeat( "<<", attributeNodeStack.depth() + 1 ), + repeat( "<<", attributeNodeStack.depth() + 1 ), attributeName ); } @@ -109,6 +85,31 @@ public AttributeNodeImplementor visitAttributeNode(GraphLanguageParser.At return attributeNode; } + private SubGraphGenerator subGraphCreator(GraphLanguageParser.AttributeQualifierContext attributeQualifierContext, String attributeName) { + if ( attributeQualifierContext == null ) { + if ( PARSING_LOGGER.isTraceEnabled() ) { + PARSING_LOGGER.tracef( + "%s Start attribute : %s", + repeat( ">>", attributeNodeStack.depth() + 1 ), + attributeName + ); + } + return PathQualifierType.VALUE.getSubGraphCreator(); + } + else { + final String qualifierName = attributeQualifierContext.ATTR_NAME().getText(); + if ( PARSING_LOGGER.isTraceEnabled() ) { + PARSING_LOGGER.tracef( + "%s Start qualified attribute : %s.%s", + repeat( ">>", attributeNodeStack.depth() + 1 ), + attributeName, + qualifierName + ); + } + return resolvePathQualifier( qualifierName ).getSubGraphCreator(); + } + } + private AttributeNodeImplementor resolveAttributeNode(String attributeName) { final var currentGraph = graphStack.getCurrent(); assert currentGraph != null; @@ -140,7 +141,7 @@ public SubGraphImplementor visitSubGraph(GraphLanguageParser.SubGraphContext if ( PARSING_LOGGER.isTraceEnabled() ) { PARSING_LOGGER.tracef( "%s Starting graph: %s", - StringHelper.repeat( ">>", attributeNodeStack.depth() + 2 ), + repeat( ">>", attributeNodeStack.depth() + 2 ), subTypeName ); } @@ -148,7 +149,7 @@ public SubGraphImplementor visitSubGraph(GraphLanguageParser.SubGraphContext final var attributeNode = attributeNodeStack.getCurrent(); final var subGraphCreator = graphSourceStack.getCurrent(); - final SubGraphImplementor subGraph = subGraphCreator.createSubGraph( + final var subGraph = subGraphCreator.createSubGraph( attributeNode, subTypeName, entityNameResolver @@ -166,7 +167,7 @@ public SubGraphImplementor visitSubGraph(GraphLanguageParser.SubGraphContext if ( PARSING_LOGGER.isTraceEnabled() ) { PARSING_LOGGER.tracef( "%s Finished graph : %s", - StringHelper.repeat( "<<", attributeNodeStack.depth() + 2 ), + repeat( "<<", attributeNodeStack.depth() + 2 ), subGraph.getGraphedType().getTypeName() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java index 332ce3885fef..514b6c3c7316 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParsing.java @@ -41,7 +41,7 @@ public static RootGraphImplementor parse( throw new InvalidGraphException( "Expecting graph text to not include an entity name: " + graphText ); } - return parse( entityDomainType, graphContext.attributeList(), sessionFactory ); + return visit( entityDomainType, graphContext.attributeList(), sessionFactory ); } public static RootGraphImplementor parse( @@ -74,30 +74,30 @@ public static RootGraphImplementor parse( final String entityName = graphContext.typeIndicator().TYPE_NAME().getText(); final var entityType = sessionFactory.getJpaMetamodel().entity( entityName ); - return parse( entityType, graphContext.attributeList(), sessionFactory ); + return visit( entityType, graphContext.attributeList(), sessionFactory ); } - public static RootGraphImplementor parse( + public static RootGraphImplementor visit( EntityDomainType rootType, GraphLanguageParser.AttributeListContext attributeListContext, SessionFactoryImplementor sessionFactory) { - return parse( rootType, attributeListContext, sessionFactory.getJpaMetamodel()::findEntityType ); + return visit( rootType, attributeListContext, sessionFactory.getJpaMetamodel()::findEntityType ); } - public static RootGraphImplementor parse( + public static RootGraphImplementor visit( EntityDomainType rootType, GraphLanguageParser.AttributeListContext attributeListContext, GraphParserEntityNameResolver entityNameResolver) { - return parse( null, rootType, attributeListContext, entityNameResolver ); + return visit( null, rootType, attributeListContext, entityNameResolver ); } - private static @NonNull GraphContext parseText(String graphText) { + public static @NonNull GraphContext parseText(String graphText) { final var lexer = new GraphLanguageLexer( CharStreams.fromString( graphText ) ); final var parser = new GraphLanguageParser( new CommonTokenStream( lexer ) ); return parser.graph(); } - public static RootGraphImplementor parse( + public static RootGraphImplementor visit( @Nullable String name, EntityDomainType rootType, GraphLanguageParser.AttributeListContext attributeListContext, From ffb7d2825312122786863531b9efdeebcbe30dd7 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 9 Dec 2025 10:33:42 +0100 Subject: [PATCH 069/181] add an additional override for consistency createQuery(), createNativeQuery() are the same --- hibernate-core/src/main/java/org/hibernate/Session.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/Session.java b/hibernate-core/src/main/java/org/hibernate/Session.java index cd4109bc9a24..d826916f1987 100644 --- a/hibernate-core/src/main/java/org/hibernate/Session.java +++ b/hibernate-core/src/main/java/org/hibernate/Session.java @@ -14,6 +14,7 @@ import jakarta.persistence.metamodel.EntityType; import org.hibernate.graph.RootGraph; import org.hibernate.jdbc.Work; +import org.hibernate.query.NativeQuery; import org.hibernate.query.Query; import org.hibernate.stat.SessionStatistics; @@ -1515,7 +1516,7 @@ public interface Session extends SharedSessionContract, EntityManager { @Override List> getEntityGraphs(Class entityClass); - // The following overrides should not be necessary, + // The following overrides should not be necessary // and are only needed to work around a bug in IntelliJ @Override @@ -1527,6 +1528,9 @@ public interface Session extends SharedSessionContract, EntityManager { @Override @Deprecated @SuppressWarnings("rawtypes") Query createQuery(String queryString); + @Override @Deprecated @SuppressWarnings("rawtypes") + NativeQuery createNativeQuery(String queryString); + @Override Query createNamedQuery(String name, Class resultClass); From b1aad7664ea0a6e5c997a5aa56e8064eca095b11 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 9 Dec 2025 11:39:17 +0100 Subject: [PATCH 070/181] HHH-19979 handle @NamedEntityGraph with defaulted name --- .../processor/annotation/AnnotationMeta.java | 33 ++++++++++++------- .../test/namedquery/AuxiliaryTest.java | 17 +++++++++- .../processor/test/namedquery/Book.java | 2 ++ 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMeta.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMeta.java index 233e9f25df18..382c900f00fe 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMeta.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMeta.java @@ -156,7 +156,7 @@ private void addAuxiliaryMembersForRepeatableAnnotation(String annotationName, S final List annotationValues = (List) value.getValue(); for ( AnnotationValue annotationValue : annotationValues ) { - addAuxiliaryMembersForMirror( (AnnotationMirror) annotationValue.getValue(), prefix ); + addAuxiliaryMembersForMirror( (AnnotationMirror) annotationValue.getValue(), prefix, annotationName ); } } } @@ -165,21 +165,32 @@ private void addAuxiliaryMembersForRepeatableAnnotation(String annotationName, S private void addAuxiliaryMembersForAnnotation(String annotationName, String prefix) { final AnnotationMirror mirror = getAnnotationMirror( getElement(), annotationName ); if ( mirror != null ) { - addAuxiliaryMembersForMirror( mirror, prefix ); + addAuxiliaryMembersForMirror( mirror, prefix, annotationName ); } } - private void addAuxiliaryMembersForMirror(AnnotationMirror mirror, String prefix) { + private void addAuxiliaryMembersForMirror(AnnotationMirror mirror, String prefix, String annotationName) { if ( !isJakartaDataStyle() ) { - mirror.getElementValues().forEach((key, value) -> { - if ( key.getSimpleName().contentEquals("name") ) { - final String name = value.getValue().toString(); - if ( !name.isEmpty() ) { - putMember( prefix + name, auxiliaryMember( mirror, prefix, name ) ); - } - } - }); + final String name = defaultImplicitName( annotationName, explicitName( mirror ) ); + putMember( prefix + name, auxiliaryMember( mirror, prefix, name ) ); + } + } + + private static String explicitName(AnnotationMirror mirror) { + for ( var entry : mirror.getElementValues().entrySet() ) { + if ( entry.getKey().getSimpleName().contentEquals( "name" ) ) { + return entry.getValue().getValue().toString(); + } } + return ""; + } + + private String defaultImplicitName(String annotationName, String explicitName) { + return explicitName.isEmpty() + && ( Constants.NAMED_ENTITY_GRAPH.equals( annotationName ) + || Constants.NAMED_ENTITY_GRAPHS.equals( annotationName ) ) + ? getSimpleName() + : explicitName; } private NameMetaAttribute auxiliaryMember(AnnotationMirror mirror, String prefix, String name) { diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/namedquery/AuxiliaryTest.java b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/namedquery/AuxiliaryTest.java index c031fd4baaed..1d9d44dc4830 100644 --- a/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/namedquery/AuxiliaryTest.java +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/namedquery/AuxiliaryTest.java @@ -98,7 +98,22 @@ void test() { assertPresenceOfNameFieldInMetamodelFor( Book.class, "GRAPH_ENTITY_GRAPH", - "Missing fetch profile attribute." + "Missing graph attribute." + ); + assertPresenceOfFieldInMetamodelFor( + Book.class, + "_entityGraph", + "Missing graph attribute." + ); + assertPresenceOfNameFieldInMetamodelFor( + Book.class, + "GRAPH_BOOK", + "Missing defaulted graph attribute." + ); + assertPresenceOfFieldInMetamodelFor( + Book.class, + "_Book", + "Missing defaulted graph attribute." ); assertPresenceOfMethodInMetamodelFor( Book.class, diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/namedquery/Book.java b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/namedquery/Book.java index d19d94a01ada..7e8eb70b7f7a 100644 --- a/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/namedquery/Book.java +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/namedquery/Book.java @@ -6,11 +6,13 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; +import jakarta.persistence.NamedAttributeNode; import jakarta.persistence.NamedEntityGraph; import jakarta.persistence.NamedQuery; @Entity @NamedEntityGraph(name = "entityGraph") +@NamedEntityGraph(attributeNodes = @NamedAttributeNode("text")) @NamedQuery(name="booksByTitle", query = "from Book where title = ?1") @NamedQuery(name="booksByTitleVerbose", From 186aa16cd3c34bc409e7744f7fa39ed9db35528d Mon Sep 17 00:00:00 2001 From: Werner Altewischer Date: Tue, 25 Nov 2025 07:53:41 +0100 Subject: [PATCH 071/181] HHH-19955 Fix for NullpointerException in EntityEntryContext in some multi-threaded scenarios --- .../engine/internal/EntityEntryContext.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java index 535836d0a35e..fcf4decfeff3 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java @@ -161,14 +161,13 @@ private static boolean isReferenceCachingEnabled(EntityPersister persister) { private ManagedEntity getAssociatedManagedEntity(Object entity) { if ( isManagedEntity( entity ) ) { final var managedEntity = asManagedEntity( entity ); - if ( managedEntity.$$_hibernate_getEntityEntry() == null ) { - // it is not associated - return null; - } final var entityEntry = (EntityEntryImpl) managedEntity.$$_hibernate_getEntityEntry(); - + if ( entityEntry == null ) { + // it is not associated + return null; + } if ( entityEntry.getPersister().isMutable() ) { return entityEntry.getPersistenceContext() == persistenceContext ? managedEntity // it is associated @@ -449,12 +448,14 @@ public void serialize(ObjectOutputStream oos) throws IOException { var managedEntity = head; while ( managedEntity != null ) { // so we know whether or not to build a ManagedEntityImpl on deserialize - oos.writeBoolean( managedEntity == managedEntity.$$_hibernate_getEntityInstance() ); - oos.writeObject( managedEntity.$$_hibernate_getEntityInstance() ); + final var instance = managedEntity.$$_hibernate_getEntityInstance(); + oos.writeBoolean( managedEntity == instance ); + oos.writeObject( instance ); // we need to know which implementation of EntityEntry is being serialized - oos.writeInt( managedEntity.$$_hibernate_getEntityEntry().getClass().getName().length() ); - oos.writeChars( managedEntity.$$_hibernate_getEntityEntry().getClass().getName() ); - managedEntity.$$_hibernate_getEntityEntry().serialize( oos ); + final var entry = managedEntity.$$_hibernate_getEntityEntry(); + oos.writeInt( entry.getClass().getName().length() ); + oos.writeChars( entry.getClass().getName() ); + entry.serialize( oos ); managedEntity = managedEntity.$$_hibernate_getNextManagedEntity(); } } From ce1751b225fe8acbc99b26e448759697ad78dbfc Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 9 Dec 2025 18:28:01 +0100 Subject: [PATCH 072/181] move two @Internal interfaces into org.hibernate.query.internal and fix a wrong @since annotation --- .../org/hibernate/query/internal/QueryArguments.java | 2 -- .../internal/QueryInterpretationCacheStandardImpl.java | 1 - .../query/internal/QueryParameterBindingImpl.java | 2 +- .../QueryParameterBindingValidator.java | 6 ++---- .../{spi => internal}/SimpleHqlInterpretationImpl.java | 10 +++------- .../hibernate/query/spi/SqlOmittingQueryOptions.java | 2 +- 6 files changed, 7 insertions(+), 16 deletions(-) rename hibernate-core/src/main/java/org/hibernate/query/{spi => internal}/QueryParameterBindingValidator.java (97%) rename hibernate-core/src/main/java/org/hibernate/query/{spi => internal}/SimpleHqlInterpretationImpl.java (87%) diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryArguments.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryArguments.java index 872dbfe53959..a73460d87268 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryArguments.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryArguments.java @@ -15,8 +15,6 @@ import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; /** - * @since 7.3 - * * @author Gavin King */ public class QueryArguments { diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheStandardImpl.java index 7c3759472b80..5884213c5fb0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheStandardImpl.java @@ -18,7 +18,6 @@ import org.hibernate.query.spi.QueryInterpretationCache; import org.hibernate.query.spi.QueryPlan; import org.hibernate.query.spi.SelectQueryPlan; -import org.hibernate.query.spi.SimpleHqlInterpretationImpl; import org.hibernate.query.sql.spi.ParameterInterpretation; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.service.ServiceRegistry; diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java index 0acc3415c676..86a4cae69fe3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java @@ -22,7 +22,7 @@ import jakarta.persistence.TemporalType; -import static org.hibernate.query.spi.QueryParameterBindingValidator.validate; +import static org.hibernate.query.internal.QueryParameterBindingValidator.validate; import static org.hibernate.type.descriptor.java.JavaTypeHelper.isTemporal; import static org.hibernate.type.internal.BindingTypeHelper.resolveTemporalPrecision; diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindingValidator.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingValidator.java similarity index 97% rename from hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindingValidator.java rename to hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingValidator.java index 351f874e8711..7de2b06b0e42 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindingValidator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingValidator.java @@ -2,11 +2,10 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.query.spi; +package org.hibernate.query.internal; import java.util.Collection; -import org.hibernate.Internal; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.QueryArgumentException; import org.hibernate.type.BindableType; @@ -17,8 +16,7 @@ /** * @author Andrea Boriero */ -@Internal -public class QueryParameterBindingValidator { +class QueryParameterBindingValidator { private QueryParameterBindingValidator() { } diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/SimpleHqlInterpretationImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/SimpleHqlInterpretationImpl.java similarity index 87% rename from hibernate-core/src/main/java/org/hibernate/query/spi/SimpleHqlInterpretationImpl.java rename to hibernate-core/src/main/java/org/hibernate/query/internal/SimpleHqlInterpretationImpl.java index 9aecd24816db..120bceae71f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/SimpleHqlInterpretationImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/SimpleHqlInterpretationImpl.java @@ -2,11 +2,12 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.query.spi; +package org.hibernate.query.internal; import java.util.concurrent.ConcurrentHashMap; -import org.hibernate.Internal; +import org.hibernate.query.spi.HqlInterpretation; +import org.hibernate.query.spi.ParameterMetadataImplementor; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; @@ -17,13 +18,8 @@ /** * Default implementation if {@link HqlInterpretation}. * - * @apiNote This class is now considered internal implementation - * and will move to an internal package in a future version. - * Application programs should never depend directly on this class. - * * @author Steve Ebersole */ -@Internal public class SimpleHqlInterpretationImpl implements HqlInterpretation { private final SqmStatement sqmStatement; private final ParameterMetadataImplementor parameterMetadata; diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/SqlOmittingQueryOptions.java b/hibernate-core/src/main/java/org/hibernate/query/spi/SqlOmittingQueryOptions.java index 14aed357672e..93f2493b6ed4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/SqlOmittingQueryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/SqlOmittingQueryOptions.java @@ -16,7 +16,7 @@ * * @author Christian Beikov */ -@Internal +@Internal // used by Hibernate Reactive public class SqlOmittingQueryOptions extends DelegatingQueryOptions { private final boolean omitLimit; From da91390eb47b8800bbd4bfcd7db876d1066b7868 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 9 Dec 2025 22:39:35 +0100 Subject: [PATCH 073/181] rethink of the previous approach to query parameter binding validation --- .../query/internal/QueryArguments.java | 38 +++- .../internal/QueryParameterBindingImpl.java | 173 +++++++++--------- .../QueryParameterBindingValidator.java | 8 +- .../query/spi/QueryParameterBinding.java | 22 ++- .../type/internal/BindingTypeHelper.java | 43 +++-- .../cid/EmbeddedIdInParameterBindingTest.java | 2 +- 6 files changed, 164 insertions(+), 122 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryArguments.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryArguments.java index a73460d87268..b11e89d9416d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryArguments.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryArguments.java @@ -21,10 +21,13 @@ public class QueryArguments { private static boolean isInstance(Object value, JavaType javaType) { try { - // special handling for entity arguments due to - // the possibility of an uninitialized proxy - // (which we don't want or need to fetch) - if ( javaType instanceof EntityJavaType ) { + if ( value == null ) { + return true; + } + else if ( javaType instanceof EntityJavaType ) { + // special handling for entity arguments due to + // the possibility of an uninitialized proxy + // (which we don't want or need to fetch) final var javaTypeClass = javaType.getJavaTypeClass(); final var initializer = extractLazyInitializer( value ); final var valueEntityClass = @@ -94,4 +97,31 @@ public static boolean areInstances( } return true; } + + public static T cast(Object value, JavaType javaType) { + if ( value == null ) { + return null; + } + else if ( javaType instanceof EntityJavaType ) { + // special handling for entity arguments due to + // the possibility of an uninitialized proxy + // (which we don't want or need to fetch) + if ( isInstance( value, javaType ) ) { + // The proxy might not literally be an + // instance of the entity class represented + // by the unreified type T, but it is an + // instance in spirit + //noinspection unchecked + return (T) value; + } + else { + throw new ClassCastException( "Cannot cast to entity type '" + + javaType.getJavaTypeClass().getTypeName() + "'" ); + } + } + else { + // require that the argument be assignable to the parameter + return javaType.cast( javaType.coerce( value ) ); + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java index 86a4cae69fe3..aaed23992b53 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java @@ -22,7 +22,6 @@ import jakarta.persistence.TemporalType; -import static org.hibernate.query.internal.QueryParameterBindingValidator.validate; import static org.hibernate.type.descriptor.java.JavaTypeHelper.isTemporal; import static org.hibernate.type.internal.BindingTypeHelper.resolveTemporalPrecision; @@ -38,11 +37,11 @@ public class QueryParameterBindingImpl implements QueryParameterBinding { private boolean isBound; private boolean isMultiValued; - private @Nullable BindableType bindType; + private @Nullable BindableType bindType; private @Nullable MappingModelExpressible type; - private @Nullable TemporalType explicitTemporalPrecision; + private @Nullable @SuppressWarnings("deprecation") TemporalType explicitTemporalPrecision; - private Object bindValue; + private T bindValue; private Collection bindValues; /** @@ -60,7 +59,7 @@ protected QueryParameterBindingImpl( public QueryParameterBindingImpl( QueryParameter queryParameter, SessionFactoryImplementor sessionFactory, - BindableType bindType) { + @Nullable BindableType bindType) { this.queryParameter = queryParameter; this.sessionFactory = sessionFactory; this.bindType = bindType; @@ -70,13 +69,17 @@ private QueryParameterBindingTypeResolver getParameterBindingTypeResolver() { return sessionFactory.getMappingMetamodel(); } + public TypeConfiguration getTypeConfiguration() { + return sessionFactory.getTypeConfiguration(); + } + @Override - public @Nullable BindableType getBindType() { + public @Nullable BindableType getBindType() { return bindType; } @Override - public @Nullable TemporalType getExplicitTemporalPrecision() { + public @Nullable @SuppressWarnings("deprecation") TemporalType getExplicitTemporalPrecision() { return explicitTemporalPrecision; } @@ -107,17 +110,14 @@ public T getBindValue() { if ( isMultiValued ) { throw new IllegalStateException( "Binding is multi-valued; illegal call to #getBindValue" ); } - - //TODO: I believe this cast is unsound due to coercion - return (T) bindValue; + return bindValue; } @Override - public void setBindValue(T value, boolean resolveJdbcTypeIfNecessary) { + public void setBindValue(Object value, boolean resolveJdbcTypeIfNecessary) { if ( !handleAsMultiValue( value, null ) ) { - final Object coerced = coerceIfNotJpa( value ); - validate( getBindType(), coerced, sessionFactory ); - + final Object coerced = coerce( value ); + validate( coerced ); if ( value == null ) { // needed when setting a null value to the parameter of a native SQL query // TODO: this does not look like a very disciplined way to handle this @@ -129,70 +129,73 @@ public void setBindValue(T value, boolean resolveJdbcTypeIfNecessary) { } } - private void bindNull(boolean resolveJdbcTypeIfNecessary) { - isBound = true; - bindValue = null; - if ( resolveJdbcTypeIfNecessary && bindType == null ) { - bindType = (BindableType) - getTypeConfiguration().getBasicTypeRegistry() - .getRegisteredType( "null" ); - } - } - - private boolean handleAsMultiValue(T value, @Nullable BindableType bindableType) { - if ( queryParameter.allowsMultiValuedBinding() - && value instanceof Collection - && !( bindableType == null - ? isRegisteredAsBasicType( value.getClass() ) - : bindableType.getJavaType().isInstance( value ) ) ) { - //noinspection unchecked - setBindValues( (Collection) value ); - return true; - } - else { - return false; - } - } - - private boolean isRegisteredAsBasicType(Class valueClass) { - return getTypeConfiguration().getBasicTypeForJavaType( valueClass ) != null; - } - - private void bindValue(Object value) { - isBound = true; - bindValue = value; - if ( canBindValueBeSet( value, bindType ) ) { - bindType = getParameterBindingTypeResolver().resolveParameterBindType( value ); - } - } - @Override - public void setBindValue(T value, @Nullable BindableType clarifiedType) { + public void setBindValue(Object value, @Nullable BindableType clarifiedType) { if ( !handleAsMultiValue( value, clarifiedType ) ) { if ( clarifiedType != null ) { bindType = clarifiedType; } final Object coerced = coerce( value ); - validate( clarifiedType, coerced, sessionFactory ); + validate( coerced ); bindValue( coerced ); } } @Override - public void setBindValue(T value, TemporalType temporalTypePrecision) { + public void setBindValue(Object value, @SuppressWarnings("deprecation") TemporalType temporalTypePrecision) { if ( !handleAsMultiValue( value, null ) ) { if ( bindType == null ) { bindType = queryParameter.getHibernateType(); } - final Object coerced = coerceIfNotJpa( value ); - validate( getBindType(), coerced, sessionFactory ); + final Object coerced = coerce( value ); + validate( coerced ); bindValue( coerced ); setExplicitTemporalPrecision( temporalTypePrecision ); } } + private void bindValue(Object value) { + if ( canBindValueBeSet( value, bindType ) ) { + bindType = (BindableType) (BindableType) + getParameterBindingTypeResolver() + .resolveParameterBindType( value ); + } + bindValue = cast( value ); + isBound = true; + } + + private void bindNull(boolean resolveJdbcTypeIfNecessary) { + isBound = true; + bindValue = null; + if ( resolveJdbcTypeIfNecessary && bindType == null ) { + final var nullType = + getTypeConfiguration().getBasicTypeRegistry() + .getRegisteredType( "null" ); + //noinspection unchecked + bindType = (BindableType) nullType; + } + } + + private boolean handleAsMultiValue(Object value, @Nullable BindableType bindableType) { + if ( queryParameter.allowsMultiValuedBinding() + && value instanceof Collection + && !( bindableType == null + ? isRegisteredAsBasicType( value.getClass() ) + : bindableType.getJavaType().isInstance( value ) ) ) { + setBindValues( (Collection) value ); + return true; + } + else { + return false; + } + } + + private boolean isRegisteredAsBasicType(Class valueClass) { + return getTypeConfiguration().getBasicTypeForJavaType( valueClass ) != null; + } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // multi-valued binding support @@ -205,22 +208,27 @@ public Collection getBindValues() { } @Override - public void setBindValues(Collection values) { + public void setBindValues(Collection values) { if ( !queryParameter.allowsMultiValuedBinding() ) { throw new IllegalArgumentException( "Illegal attempt to bind a collection value to a single-valued parameter" ); } - this.isBound = true; - this.isMultiValued = true; + final var coerced = values.stream().map( this::coerce ).toList(); + values.forEach( this::validate ); - this.bindValue = null; - this.bindValues = values; + isBound = true; + isMultiValued = true; - final T value = firstNonNull( values ); + bindValue = null; + bindValues = coerced.stream().map( this::cast ).toList(); + + final T value = firstNonNull( bindValues ); if ( canBindValueBeSet( value, bindType ) ) { - bindType = getParameterBindingTypeResolver().resolveParameterBindType( value ); + bindType = (BindableType) (BindableType) + getParameterBindingTypeResolver() + .resolveParameterBindType( value ); } } @@ -234,7 +242,7 @@ public void setBindValues(Collection values) { } @Override - public void setBindValues(Collection values, BindableType clarifiedType) { + public void setBindValues(Collection values, BindableType clarifiedType) { if ( clarifiedType != null ) { bindType = clarifiedType; } @@ -243,14 +251,14 @@ public void setBindValues(Collection values, BindableType clarif @Override public void setBindValues( - Collection values, - TemporalType temporalTypePrecision, + Collection values, + @SuppressWarnings("deprecation") TemporalType temporalTypePrecision, TypeConfiguration typeConfiguration) { setBindValues( values ); setExplicitTemporalPrecision( temporalTypePrecision ); } - private void setExplicitTemporalPrecision(TemporalType precision) { + private void setExplicitTemporalPrecision(@SuppressWarnings("deprecation") TemporalType precision) { explicitTemporalPrecision = precision; if ( bindType == null || isTemporal( determineJavaType( bindType ) ) ) { bindType = resolveTemporalPrecision( precision, bindType, getCriteriaBuilder() ); @@ -273,14 +281,14 @@ public boolean setType(@Nullable MappingModelExpressible type) { if ( bindType == null || bindType.getJavaType() == Object.class || type instanceof ModelPart ) { if ( type instanceof BindableType ) { final boolean changed = bindType != null && type != bindType; - bindType = (BindableType) type; + bindType = (BindableType) type; return changed; } else if ( type instanceof BasicValuedMapping basicValuedMapping ) { final var jdbcMapping = basicValuedMapping.getJdbcMapping(); if ( jdbcMapping instanceof BindableType ) { final boolean changed = bindType != null && jdbcMapping != bindType; - bindType = (BindableType) jdbcMapping; + bindType = (BindableType) jdbcMapping; return changed; } } @@ -288,17 +296,18 @@ else if ( type instanceof BasicValuedMapping basicValuedMapping ) { return false; } - public TypeConfiguration getTypeConfiguration() { - return sessionFactory.getTypeConfiguration(); + private T cast(Object value) { + final var bindableType = getCriteriaBuilder().resolveExpressible( bindType ); + return bindableType == null + ? (T) value // YOLO + : QueryArguments.cast( value, bindableType.getExpressibleJavaType() ); } - private Object coerceIfNotJpa(T value) { - return sessionFactory.getSessionFactoryOptions().getJpaCompliance().isLoadByIdComplianceEnabled() - ? value - : coerce( value ); + private void validate(Object value) { + QueryParameterBindingValidator.validate( getBindType(), value, sessionFactory ); } - private Object coerce(T value) { + private Object coerce(Object value) { try { if ( canValueBeCoerced( bindType ) ) { return coerce( value, bindType ); @@ -322,14 +331,10 @@ else if ( canValueBeCoerced( queryParameter.getHibernateType() ) ) { } } - private Object coerce(T value, BindableType parameterType) { - if ( value == null ) { - return null; - } - else { - return getCriteriaBuilder().resolveExpressible( parameterType ) - .getExpressibleJavaType().coerce( value ); - } + private Object coerce(Object value, BindableType parameterType) { + return value == null ? null + : getCriteriaBuilder().resolveExpressible( parameterType ) + .getExpressibleJavaType().coerce( value ); } private static boolean canValueBeCoerced(BindableType bindType) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingValidator.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingValidator.java index 7de2b06b0e42..5437aa5f67bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingValidator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingValidator.java @@ -27,10 +27,10 @@ public static void validate(BindableType parameterType, Object argument, Sess if ( parameterJavaType != null ) { if ( argument instanceof Collection collection && !Collection.class.isAssignableFrom( parameterJavaType ) ) { - // we have a collection passed in where we are expecting a non-collection. - // NOTE: this can happen in Hibernate's notion of "parameter list" binding - // NOTE2: the case of a collection value and an expected collection (if that can even happen) - // will fall through to the main check. + // We have a collection passed in where we were expecting a non-collection. + // NOTE: This can happen in Hibernate's notion of "parameter list" binding. + // NOTE2: The case of a collection value and an expected collection + // (if that can even happen) will fall through to the main check. validateCollectionValuedParameterBinding( parameterType, parameterJavaType, collection, factory ); } else if ( argument.getClass().isArray() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBinding.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBinding.java index 703d64f0324a..bcef863253b0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBinding.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBinding.java @@ -40,18 +40,18 @@ public interface QueryParameterBinding { * * @return The currently associated Type */ - @Nullable BindableType getBindType(); + @Nullable BindableType getBindType(); /** * If the parameter represents a temporal type, return the explicitly * specified precision - if one. */ - @Nullable TemporalType getExplicitTemporalPrecision(); + @Nullable @SuppressWarnings("deprecation") TemporalType getExplicitTemporalPrecision(); /** * Sets the parameter binding value. The inherent parameter type (if known) is assumed */ - default void setBindValue(T value) { + default void setBindValue(Object value) { setBindValue( value, false ); } @@ -59,21 +59,21 @@ default void setBindValue(T value) { * Sets the parameter binding value. The inherent parameter type (if known) is assumed. * The flag controls whether the parameter type should be resolved if necessary. */ - void setBindValue(T value, boolean resolveJdbcTypeIfNecessary); + void setBindValue(Object value, boolean resolveJdbcTypeIfNecessary); /** * Sets the parameter binding value using the explicit Type. * @param value The bind value * @param clarifiedType The explicit Type to use */ - void setBindValue(T value, @Nullable BindableType clarifiedType); + void setBindValue(Object value, @Nullable BindableType clarifiedType); /** * Sets the parameter binding value using the explicit TemporalType. * @param value The bind value * @param temporalTypePrecision The temporal type to use */ - void setBindValue(T value, TemporalType temporalTypePrecision); + void setBindValue(Object value, @SuppressWarnings("deprecation") TemporalType temporalTypePrecision); /** * Get the value current bound. @@ -88,21 +88,25 @@ default void setBindValue(T value) { * @param values The bind values * */ - void setBindValues(Collection values); + void setBindValues(Collection values); /** * Sets the parameter binding values using the explicit Type in regards to the individual values. * @param values The bind values * @param clarifiedType The explicit Type to use */ - void setBindValues(Collection values, BindableType clarifiedType); + void setBindValues(Collection values, BindableType clarifiedType); /**Sets the parameter binding value using the explicit TemporalType in regards to the individual values. * * @param values The bind values * @param temporalTypePrecision The temporal type to use */ - void setBindValues(Collection values, TemporalType temporalTypePrecision, TypeConfiguration typeConfiguration); + void setBindValues( + Collection values, + @SuppressWarnings("deprecation") + TemporalType temporalTypePrecision, + TypeConfiguration typeConfiguration); /** * Get the values currently bound. diff --git a/hibernate-core/src/main/java/org/hibernate/type/internal/BindingTypeHelper.java b/hibernate-core/src/main/java/org/hibernate/type/internal/BindingTypeHelper.java index e99538ad1875..5f09df40d16a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/internal/BindingTypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/internal/BindingTypeHelper.java @@ -14,14 +14,13 @@ import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.type.BindableType; import org.hibernate.type.BindingContext; -import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.type.StandardBasicTypes; -import org.hibernate.type.descriptor.java.JavaTypeHelper; import org.hibernate.type.descriptor.java.TemporalJavaType; import org.hibernate.type.spi.TypeConfiguration; import jakarta.persistence.TemporalType; +import static org.hibernate.type.descriptor.java.JavaTypeHelper.isTemporal; import static org.hibernate.type.descriptor.java.TemporalJavaType.resolveJavaTypeClass; import static org.hibernate.type.descriptor.java.TemporalJavaType.resolveJdbcTypeCode; @@ -33,14 +32,14 @@ private BindingTypeHelper() { } public static BindableType resolveTemporalPrecision( - TemporalType precision, + @SuppressWarnings("deprecation") TemporalType precision, BindableType declaredParameterType, BindingContext bindingContext) { if ( precision != null ) { - final TemporalJavaType temporalJtd = getTemporalJavaType( declaredParameterType, bindingContext ); + final var temporalJtd = getTemporalJavaType( declaredParameterType, bindingContext ); if ( temporalJtd == null || temporalJtd.getPrecision() != precision ) { - final TypeConfiguration typeConfiguration = bindingContext.getTypeConfiguration(); - final TemporalJavaType temporalTypeForPrecision = + final var typeConfiguration = bindingContext.getTypeConfiguration(); + final var temporalTypeForPrecision = getTemporalTypeForPrecision( precision, temporalJtd, typeConfiguration ); return typeConfiguration.getBasicTypeRegistry() .resolve( temporalTypeForPrecision, resolveJdbcTypeCode( precision ) ); @@ -50,11 +49,14 @@ public static BindableType resolveTemporalPrecision( } private static TemporalJavaType getTemporalTypeForPrecision( - TemporalType precision, TemporalJavaType temporalJtd, TypeConfiguration typeConfiguration) { + @SuppressWarnings("deprecation") TemporalType precision, + TemporalJavaType temporalJtd, + TypeConfiguration typeConfiguration) { // Special case java.util.Date, because TemporalJavaType#resolveTypeForPrecision doesn't support widening, // since the main purpose of that method is to determine the final java type based on the reflective type // + the explicit @Temporal(TemporalType...) configuration - if ( temporalJtd == null || java.util.Date.class.isAssignableFrom( temporalJtd.getJavaTypeClass() ) ) { + if ( temporalJtd == null + || java.util.Date.class.isAssignableFrom( temporalJtd.getJavaTypeClass() ) ) { final var descriptor = typeConfiguration.getJavaTypeRegistry() .resolveDescriptor( resolveJavaTypeClass( precision ) ); @@ -69,13 +71,15 @@ private static TemporalJavaType getTemporalTypeForPrecision( private static TemporalJavaType getTemporalJavaType( BindableType declaredParameterType, BindingContext bindingContext) { if ( declaredParameterType != null ) { - final SqmExpressible sqmExpressible = bindingContext.resolveExpressible( declaredParameterType ); - if ( !( JavaTypeHelper.isTemporal( sqmExpressible.getExpressibleJavaType() ) ) ) { + final var javaType = + bindingContext.resolveExpressible( declaredParameterType ) + .getExpressibleJavaType(); + if ( !isTemporal( javaType ) ) { throw new UnsupportedOperationException( "Cannot treat non-temporal parameter type with temporal precision" ); } - return (TemporalJavaType) sqmExpressible.getExpressibleJavaType(); + return (TemporalJavaType) javaType; } else { return null; @@ -86,18 +90,17 @@ public static JdbcMapping resolveBindType( Object value, JdbcMapping baseType, TypeConfiguration typeConfiguration) { - if ( value == null || !JavaTypeHelper.isTemporal( baseType.getJdbcJavaType() ) ) { + if ( value == null || !isTemporal( baseType.getJdbcJavaType() ) ) { return baseType; } else { - final Class javaType = value.getClass(); - final TemporalJavaType temporalJavaType = (TemporalJavaType) baseType.getJdbcJavaType(); - final TemporalType temporalType = temporalJavaType.getPrecision(); - final BindableType bindableType = (BindableType) baseType; - return switch ( temporalType ) { - case TIMESTAMP -> (JdbcMapping) resolveTimestampTemporalTypeVariant( javaType, bindableType, typeConfiguration ); - case DATE -> (JdbcMapping) resolveDateTemporalTypeVariant( javaType, bindableType, typeConfiguration ); - case TIME -> (JdbcMapping) resolveTimeTemporalTypeVariant( javaType, bindableType, typeConfiguration ); + final var javaType = value.getClass(); + final var temporalJavaType = (TemporalJavaType) baseType.getJdbcJavaType(); + final var bindableType = (BindableType) baseType; + return (JdbcMapping) switch ( temporalJavaType.getPrecision() ) { + case TIMESTAMP -> resolveTimestampTemporalTypeVariant( javaType, bindableType, typeConfiguration ); + case DATE -> resolveDateTemporalTypeVariant( javaType, bindableType, typeConfiguration ); + case TIME -> resolveTimeTemporalTypeVariant( javaType, bindableType, typeConfiguration ); }; } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/EmbeddedIdInParameterBindingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cid/EmbeddedIdInParameterBindingTest.java index 83f29afcfe38..fb75103c60ba 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/cid/EmbeddedIdInParameterBindingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cid/EmbeddedIdInParameterBindingTest.java @@ -87,7 +87,7 @@ public void testQueryWithWhereClauseContainingInOperatorWithListOfParametersValu session -> { List deliveries = session.createQuery( "from Delivery d where d.locationId in ?1", Delivery.class ) - .setParameter( 1, List.of( verbania, schnitzel ) ) + .setParameter( 1, List.of( verbania, hallein ) ) .getResultList(); assertThat( deliveries.size() ).isEqualTo( 2 ); assertThat( deliveries ).contains( pizza ); From 0722f2c003d6fc002585d302b141b110826fd824 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 9 Dec 2025 23:09:36 +0100 Subject: [PATCH 074/181] rethink of the previous approach to query parameter binding validation --- .../internal/QueryParameterBindingImpl.java | 2 +- .../QueryParameterBindingValidator.java | 64 ++++++++++++------- .../ObjectParameterTypeForEmbeddableTest.java | 2 +- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java index aaed23992b53..ba9f4cf55de0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java @@ -304,7 +304,7 @@ private T cast(Object value) { } private void validate(Object value) { - QueryParameterBindingValidator.validate( getBindType(), value, sessionFactory ); + QueryParameterBindingValidator.validate( queryParameter, bindType, value, sessionFactory ); } private Object coerce(Object value) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingValidator.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingValidator.java index 5437aa5f67bd..5e8b8ca992a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingValidator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingValidator.java @@ -6,8 +6,10 @@ import java.util.Collection; +import org.checkerframework.checker.nullness.qual.NonNull; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.QueryArgumentException; +import org.hibernate.query.QueryParameter; import org.hibernate.type.BindableType; import static org.hibernate.query.internal.QueryArguments.areInstances; @@ -21,30 +23,41 @@ class QueryParameterBindingValidator { private QueryParameterBindingValidator() { } - public static void validate(BindableType parameterType, Object argument, SessionFactoryImplementor factory) { + public static void validate( + QueryParameter parameter, + BindableType parameterType, + Object argument, + SessionFactoryImplementor factory) { if ( argument != null && parameterType != null ) { final var parameterJavaType = getParameterJavaType( parameterType, factory ); if ( parameterJavaType != null ) { + final var criteriaBuilder = factory.getQueryEngine().getCriteriaBuilder(); if ( argument instanceof Collection collection && !Collection.class.isAssignableFrom( parameterJavaType ) ) { // We have a collection passed in where we were expecting a non-collection. // NOTE: This can happen in Hibernate's notion of "parameter list" binding. // NOTE2: The case of a collection value and an expected collection // (if that can even happen) will fall through to the main check. - validateCollectionValuedParameterBinding( parameterType, parameterJavaType, collection, factory ); + if ( !areInstances( parameterType, collection, criteriaBuilder ) ) { + throw queryArgumentException( parameterJavaType, collection, parameter ); + } } - else if ( argument.getClass().isArray() ) { - validateArrayValuedParameterBinding( parameterJavaType, argument ); + else if ( !argument.getClass().isArray() ) { + // assume single-valued argument + if ( !isInstance( parameterType, argument, criteriaBuilder ) ) { + throw queryArgumentException( parameterJavaType, argument, parameter ); + } } else { - validateSingleValuedParameterBinding( parameterType, parameterJavaType, argument, factory ); + validateArrayValuedParameterBinding( parameterJavaType, argument, parameter ); } } // else nothing we can check } } - private static Class getParameterJavaType(BindableType parameterType, SessionFactoryImplementor factory) { + private static Class getParameterJavaType( + BindableType parameterType, SessionFactoryImplementor factory) { final var javaType = parameterType.getJavaType(); return javaType != null ? javaType @@ -53,30 +66,37 @@ private static Class getParameterJavaType(BindableType parameterType, Sess .getJavaType(); } - private static void validateSingleValuedParameterBinding( - BindableType parameterType, Class parameterJavaType, - Object value, - SessionFactoryImplementor factory) { - if ( !isInstance( parameterType, value, - factory.getQueryEngine().getCriteriaBuilder() ) ) { - throw new QueryArgumentException( "Argument did not match parameter type", + private static @NonNull QueryArgumentException queryArgumentException( + Class parameterJavaType, Object value, QueryParameter parameter) { + if ( parameter.isNamed() ) { + return new QueryArgumentException( "Argument to parameter named '" + + parameter.getName() + "' has an element with an incompatible type", + parameterJavaType, value ); + } + else { + return new QueryArgumentException( "Argument to parameter at position " + + parameter.isOrdinal() + " has an element with an incompatible type", parameterJavaType, value ); } } - private static void validateCollectionValuedParameterBinding( - BindableType parameterType, Class parameterJavaType, - Collection values, - SessionFactoryImplementor factory) { - if ( !areInstances( parameterType, values, - factory.getQueryEngine().getCriteriaBuilder() ) ) { - throw new QueryArgumentException( "Collection-valued argument did not match parameter type", + private static @NonNull QueryArgumentException queryArgumentException( + Class parameterJavaType, Collection values, QueryParameter parameter) { + if ( parameter.isNamed() ) { + return new QueryArgumentException( "Collection-values argument to parameter named '" + + parameter.getName() + "' has an incompatible type", + parameterJavaType, values ); + } + else { + return new QueryArgumentException( "Collection-values argument to parameter at position " + + parameter.isOrdinal() + " has has an incompatible type", parameterJavaType, values ); - } } - private static void validateArrayValuedParameterBinding(Class parameterType, Object value) { + private static void validateArrayValuedParameterBinding( + Class parameterType, Object value, QueryParameter parameter) { + // TODO: improve the error messages using the given parameter info if ( !parameterType.isArray() ) { throw new QueryArgumentException( "Unexpected array-valued parameter binding", parameterType, value ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/ObjectParameterTypeForEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/ObjectParameterTypeForEmbeddableTest.java index d03db16a5992..d94a5f9ffe03 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/ObjectParameterTypeForEmbeddableTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/ObjectParameterTypeForEmbeddableTest.java @@ -97,7 +97,7 @@ public void testSettingParameterOfTypeWrongType(EntityManagerFactoryScope scope) ) ); - assertThat( thrown.getMessage() ).contains( "did not match parameter type" ); + assertThat( thrown.getMessage() ).contains( "incompatible type" ); } @Entity(name = "TestEntity") From 8496f3c34642c5d8719c927b45b70aacae573f4d Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Tue, 9 Dec 2025 14:57:52 +0100 Subject: [PATCH 075/181] HHH-19980 Keep non-BulkOperationCleanupAction in the after completion queue --- .../internal/AfterTransactionCompletionProcessQueue.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/AfterTransactionCompletionProcessQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/AfterTransactionCompletionProcessQueue.java index ddcd55f70d84..daf2ba616261 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/AfterTransactionCompletionProcessQueue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/AfterTransactionCompletionProcessQueue.java @@ -12,6 +12,7 @@ import org.hibernate.engine.spi.TransactionCompletionCallbacks.AfterCompletionCallback; import java.util.HashSet; +import java.util.Iterator; import java.util.Set; import static org.hibernate.internal.CoreMessageLogger.CORE_LOGGER; @@ -62,13 +63,15 @@ void afterTransactionCompletion(boolean success) { } void executePendingBulkOperationCleanUpActions() { - AfterCompletionCallback process; boolean hasPendingBulkOperationCleanUpActions = false; - while ( ( process = processes.poll() ) != null ) { + Iterator iterator = processes.iterator(); + while ( iterator.hasNext() ) { + AfterCompletionCallback process = iterator.next(); if ( process instanceof BulkOperationCleanupAction.BulkOperationCleanUpAfterTransactionCompletionProcess ) { try { hasPendingBulkOperationCleanUpActions = true; process.doAfterTransactionCompletion( true, session ); + iterator.remove(); } catch (CacheException ce) { CORE_LOGGER.unableToReleaseCacheLock( ce ); From 8e1bf209e22234c3f03dc1c02b8493754b47b51c Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 10 Dec 2025 11:37:10 -0700 Subject: [PATCH 076/181] small fix in README --- release/README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/README.adoc b/release/README.adoc index 7748b8c7f005..567abd2a33b7 100644 --- a/release/README.adoc +++ b/release/README.adoc @@ -3,7 +3,7 @@ == Where is the information -If you're looking for information about how to release Hibernate ORM, see link:/MAINTAINERS.md[MAINTAINERS.md] +If you're looking for information about how to release Hibernate ORM, see link:../MAINTAINERS.md[MAINTAINERS.md] If you're looking for information about how releases are implemented technically, read on. From cb6d2ce751beab428ed8ee8ec7f6ebca1ac67f6c Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Wed, 10 Dec 2025 18:49:35 +0000 Subject: [PATCH 077/181] [Jenkins release job] changelog.txt updated by release build 7.2.0.CR4 --- changelog.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/changelog.txt b/changelog.txt index 5335460b4196..eacba2aab168 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,28 @@ Hibernate 7 Changelog ======================= +Changes in 7.2.0.CR4 (December 10, 2025) +------------------------------------------------------------------------------------------------------------------------ + +https://hibernate.atlassian.net/projects/HHH/versions/36476 + + +** Bug + * HHH-19980 In JTA after-completion callbacks may get ignored + * HHH-19979 processor should handle @NamedEntityGraph with defaulted name + * HHH-19975 Calling entityManager.find(clazz, id) with null id throws NullPointerException + * HHH-19972 OptionalTableUpdateOperation can fail on PostgreSQL < 15 and CockroachDB + * HHH-19963 Wrong references in entity fields with circular associations + * HHH-19958 `` tag in orm.xml is not implemented + * HHH-19955 Thread-safety issue in EntityEntryContext resulting in NullPointerException for Session.contains() calls. + * HHH-19843 Bean Validation may fail on operations with stateless session + * HHH-18871 Nested NativeQuery mappings causing 'Could not locate TableGroup' exception after migration + * HHH-18217 StatelessSession.upsert() for entity with all-null non-id fields, or no non-id field + +** Improvement + * HHH-19943 Comparison of generic nested EmbeddedId's fails for JPQL and Criteria API + * HHH-19215 Extends Dialect#addQueryHints to support straight_join syntax + Changes in 7.2.0.CR3 (November 25, 2025) ------------------------------------------------------------------------------------------------------------------------ From 1a11d3f91c296e9d6dcbd036bbb01565da9af9bf Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Wed, 10 Dec 2025 18:49:36 +0000 Subject: [PATCH 078/181] [Jenkins release job] Preparing release 7.2.0.CR4 --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index 8f31599e762f..508563ad0b91 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -hibernateVersion=7.2.0-SNAPSHOT \ No newline at end of file +hibernateVersion=7.2.0.CR4 \ No newline at end of file From f144b9fe17d18b8630921c330a7bb5fb784adb5e Mon Sep 17 00:00:00 2001 From: Hibernate-CI Date: Wed, 10 Dec 2025 19:07:35 +0000 Subject: [PATCH 079/181] [Jenkins release job] Preparing next development iteration --- gradle/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/version.properties b/gradle/version.properties index 508563ad0b91..8f31599e762f 100644 --- a/gradle/version.properties +++ b/gradle/version.properties @@ -1 +1 @@ -hibernateVersion=7.2.0.CR4 \ No newline at end of file +hibernateVersion=7.2.0-SNAPSHOT \ No newline at end of file From cbcdd23db69b6a758f4e9f774217a82d3e059142 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 11 Dec 2025 14:29:26 -0700 Subject: [PATCH 080/181] HHH-7287 - Problem in caching proper natural-id-values when obtaining result by naturalIdQuery --- .../mutable/MutableNaturalIdTest.java | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java index 36a5cf0671dd..339f9a719731 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java @@ -29,6 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; /** * @author Gavin King @@ -58,7 +59,7 @@ public void testNaturalIdNullability(SessionFactoryScope scope) { @AfterEach public void dropTestData(SessionFactoryScope scope) { - scope.getSessionFactory().getSchemaManager().truncate(); + scope.dropData(); } @Test @@ -362,4 +363,41 @@ public void testEviction(SessionFactoryScope scope) { } ); } + + @Test + @JiraKey("HHH-7287") + public void testModificationInOtherSession(SessionFactoryScope factoryScope) { + factoryScope.inTransaction( (session) -> { + User u = new User( "gavin", "hb", "secret" ); + session.persist( u ); + } ); + + // Use transactionless session + factoryScope.inSession( (session) -> { + // this loads the state into this `session` + var byNaturalId = session.byNaturalId( User.class ).using( "name", "gavin" ).using( "org", "hb" ).load(); + assertNotNull( byNaturalId ); + + // CHANGE natural-id values in another session + factoryScope.inTransaction( (otherSession) -> { + var u = otherSession.find( User.class, 1 ); + u.setOrg( "zz" ); + } ); + // CHANGE APPLIED + + byNaturalId = session.byNaturalId( User.class ) + .using( "name", "gavin" ) + .using( "org", "hb" ).load(); + assertNotNull( byNaturalId ); + + // the internal query will 'see' the new values, because isolation level < SERIALIZABLE + var byNaturalId2 = session.byNaturalId( User.class ) + .using( "name", "gavin" ) + .using( "org", "zz" ).load(); + assertSame( byNaturalId, byNaturalId2 ); + + // this fails, that's the bug + assertNotNull( session.byNaturalId( User.class ).using( "name", "gavin" ).using( "org", "hb" ).load()); + } ); + } } From e2b93cb91de597a3cf3c0683f400b630e83cacc3 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 11 Dec 2025 16:23:13 -0700 Subject: [PATCH 081/181] HHH-7287 - Problem in caching proper natural-id-values when obtaining result by naturalIdQuery --- .../test/mapping/naturalid/mutable/MutableNaturalIdTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java index 339f9a719731..0bca803ce384 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java @@ -367,9 +367,10 @@ public void testEviction(SessionFactoryScope scope) { @Test @JiraKey("HHH-7287") public void testModificationInOtherSession(SessionFactoryScope factoryScope) { - factoryScope.inTransaction( (session) -> { + var id = factoryScope.fromTransaction( (session) -> { User u = new User( "gavin", "hb", "secret" ); session.persist( u ); + return u.getId(); } ); // Use transactionless session @@ -380,7 +381,7 @@ public void testModificationInOtherSession(SessionFactoryScope factoryScope) { // CHANGE natural-id values in another session factoryScope.inTransaction( (otherSession) -> { - var u = otherSession.find( User.class, 1 ); + var u = otherSession.find( User.class, id ); u.setOrg( "zz" ); } ); // CHANGE APPLIED From ddb89212b8f6cc5c5c36b51fbbae1cf4463427f9 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 12 Dec 2025 14:35:10 +0100 Subject: [PATCH 082/181] Workaround JDK 17 javac bug for switch expression compilation --- .../org/hibernate/type/internal/BindingTypeHelper.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/internal/BindingTypeHelper.java b/hibernate-core/src/main/java/org/hibernate/type/internal/BindingTypeHelper.java index 5f09df40d16a..a9ca925ca7bb 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/internal/BindingTypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/internal/BindingTypeHelper.java @@ -97,10 +97,11 @@ public static JdbcMapping resolveBindType( final var javaType = value.getClass(); final var temporalJavaType = (TemporalJavaType) baseType.getJdbcJavaType(); final var bindableType = (BindableType) baseType; - return (JdbcMapping) switch ( temporalJavaType.getPrecision() ) { - case TIMESTAMP -> resolveTimestampTemporalTypeVariant( javaType, bindableType, typeConfiguration ); - case DATE -> resolveDateTemporalTypeVariant( javaType, bindableType, typeConfiguration ); - case TIME -> resolveTimeTemporalTypeVariant( javaType, bindableType, typeConfiguration ); + // Cast individual arms of the switch to avoid a JDK 17 javac bug + return switch ( temporalJavaType.getPrecision() ) { + case TIMESTAMP -> (JdbcMapping) resolveTimestampTemporalTypeVariant( javaType, bindableType, typeConfiguration ); + case DATE -> (JdbcMapping) resolveDateTemporalTypeVariant( javaType, bindableType, typeConfiguration ); + case TIME -> (JdbcMapping) resolveTimeTemporalTypeVariant( javaType, bindableType, typeConfiguration ); }; } } From d1401f76e148a0a4768f4e70e6ff339b1b978ac5 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Thu, 4 Dec 2025 17:52:13 +0100 Subject: [PATCH 083/181] HHH-19976 Don't adopt AdjustableBasicType name to create derived type Also, store BasicTypeReferences by java type name and try finding a JavaType/JdbcType match when resolving a BasicType --- .../internal/InferredBasicValueResolver.java | 12 ++-- .../process/spi/MetadataBuildingProcess.java | 5 +- .../hibernate/type/AdjustableBasicType.java | 8 +-- .../org/hibernate/type/BasicTypeRegistry.java | 65 ++++++++++++++----- .../hibernate/type/StandardBasicTypes.java | 5 +- .../mapping/basic/DurationMappingTests.java | 3 + .../integration/basic/NationalizedTest.java | 65 +++++++++++++++++++ .../orm/junit/DialectFeatureChecks.java | 6 ++ 8 files changed, 136 insertions(+), 33 deletions(-) create mode 100644 hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/basic/NationalizedTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java index e00feb694a43..7e0b38521d3d 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java @@ -68,7 +68,7 @@ public static BasicValue.Resolution from( final var typeConfiguration = bootstrapContext.getTypeConfiguration(); final var basicTypeRegistry = typeConfiguration.getBasicTypeRegistry(); - final var reflectedJtd = reflectedJtdResolver.get(); + final JavaType reflectedJtd; // NOTE: the distinction that is made below wrt `explicitJavaType` and `reflectedJtd` // is needed temporarily to trigger "legacy resolution" versus "ORM6 resolution. @@ -110,7 +110,7 @@ else if ( explicitJdbcType != null ) { } } } - else if ( reflectedJtd != null ) { + else if ( ( reflectedJtd = reflectedJtdResolver.get() ) != null ) { // we were able to determine the "reflected java-type" // Use JTD if we know it to apply any specialized resolutions if ( reflectedJtd instanceof EnumJavaType enumJavaType ) { @@ -150,7 +150,7 @@ else if ( explicitJdbcType != null ) { if ( registeredType != null ) { // so here is the legacy resolution - jdbcMapping = resolveSqlTypeIndicators( stdIndicators, registeredType, reflectedJtd ); + jdbcMapping = resolveSqlTypeIndicators( stdIndicators, registeredType, registeredType.getJavaTypeDescriptor() ); } else { // there was not a "legacy" BasicType registration, @@ -311,7 +311,11 @@ private static BasicType pluralBasicType( pluralJavaType.resolveType( bootstrapContext.getTypeConfiguration(), dialect, - resolveSqlTypeIndicators( stdIndicators, registeredElementType, elementJavaType ), + resolveSqlTypeIndicators( + stdIndicators, + registeredElementType, + registeredElementType.getJavaTypeDescriptor() + ), selectable instanceof ColumnTypeInformation information ? information : null, stdIndicators ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java index af8a90295a7a..a8b3a9443efe 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java @@ -762,7 +762,7 @@ public void contributeType(CompositeUserType type) { } final int preferredSqlTypeCodeForDuration = getPreferredSqlTypeCodeForDuration( serviceRegistry ); - if ( preferredSqlTypeCodeForDuration != SqlTypes.INTERVAL_SECOND ) { + if ( preferredSqlTypeCodeForDuration != SqlTypes.DURATION ) { adaptToPreferredSqlTypeCode( typeConfiguration, jdbcTypeRegistry, @@ -772,9 +772,6 @@ public void contributeType(CompositeUserType type) { "org.hibernate.type.DurationType" ); } - else { - addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INTERVAL_SECOND, SqlTypes.DURATION ); - } addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INET, SqlTypes.VARBINARY ); addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.GEOMETRY, SqlTypes.VARBINARY ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/AdjustableBasicType.java b/hibernate-core/src/main/java/org/hibernate/type/AdjustableBasicType.java index c456e995651f..da9f9c0a38e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/AdjustableBasicType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/AdjustableBasicType.java @@ -25,16 +25,16 @@ default BasicType resolveIndicatedType(JdbcTypeIndicators indicators, Jav indicators, domainJtd ); - if ( resolvedJdbcType != jdbcType ) { + if ( getJavaTypeDescriptor() != domainJtd || resolvedJdbcType != jdbcType ) { return indicators.getTypeConfiguration().getBasicTypeRegistry() - .resolve( domainJtd, resolvedJdbcType, getName() ); + .resolve( domainJtd, resolvedJdbcType ); } } else { final int resolvedJdbcTypeCode = indicators.resolveJdbcTypeCode( jdbcType.getDefaultSqlTypeCode() ); - if ( resolvedJdbcTypeCode != jdbcType.getDefaultSqlTypeCode() ) { + if ( getJavaTypeDescriptor() != domainJtd || resolvedJdbcTypeCode != jdbcType.getDefaultSqlTypeCode() ) { return indicators.getTypeConfiguration().getBasicTypeRegistry() - .resolve( domainJtd, indicators.getJdbcType( resolvedJdbcTypeCode ), getName() ); + .resolve( domainJtd, indicators.getJdbcType( resolvedJdbcTypeCode ) ); } } return (BasicType) this; diff --git a/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java b/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java index 4b437c0184f4..9e9070864372 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java @@ -5,6 +5,8 @@ package org.hibernate.type; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; @@ -49,6 +51,7 @@ public class BasicTypeRegistry implements Serializable { private final Map> typesByName = new ConcurrentHashMap<>(); private final Map> typeReferencesByName = new ConcurrentHashMap<>(); + private final Map>> typeReferencesByJavaTypeName = new ConcurrentHashMap<>(); public BasicTypeRegistry(TypeConfiguration typeConfiguration){ this.typeConfiguration = typeConfiguration; @@ -256,14 +259,28 @@ private BasicType createIfUnregistered( if ( registeredTypeMatches( javaType, jdbcType, registeredType ) ) { return castNonNull( registeredType ); } - else { - final var createdType = creator.get(); - register( javaType, jdbcType, createdType ); - return createdType; + // Create an ad-hoc type since the java type doesn't come from the registry and is probably explicitly defined + else if ( typeConfiguration.getJavaTypeRegistry().resolveDescriptor( javaType.getJavaType() ) == javaType ) { + final var basicTypeReferences = typeReferencesByJavaTypeName.get( javaType.getTypeName() ); + if ( basicTypeReferences != null && !basicTypeReferences.isEmpty() ) { + final var jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry(); + for ( var typeReference : basicTypeReferences ) { + if ( jdbcTypeRegistry.getDescriptor( typeReference.getSqlTypeCode() ) == jdbcType ) { + final var basicType = typesByName.get( typeReference.getName() ); + //noinspection unchecked + return registeredTypeMatches( javaType, jdbcType, basicType ) + ? (BasicType) basicType + : (BasicType) createBasicType( typeReference.getName(), typeReference ); + } + } + } } + final var createdType = creator.get(); + register( javaType, jdbcType, createdType ); + return createdType; } - private static boolean registeredTypeMatches(JavaType javaType, JdbcType jdbcType, BasicType registeredType) { + private static boolean registeredTypeMatches(JavaType javaType, JdbcType jdbcType, @Nullable BasicType registeredType) { return registeredType != null && registeredType.getJdbcType() == jdbcType && registeredType.getMappedJavaType() == javaType; @@ -334,7 +351,7 @@ public void addTypeReferenceRegistrationKey(String typeReferenceKey, String... a throw new IllegalArgumentException( "Couldn't find type reference with name: " + typeReferenceKey ); } for ( String additionalTypeReferenceKey : additionalTypeReferenceKeys ) { - typeReferencesByName.put( additionalTypeReferenceKey, basicTypeReference ); + addTypeReference( additionalTypeReferenceKey, basicTypeReference ); } } @@ -384,7 +401,7 @@ public void addPrimeEntry(BasicTypeReference type, String legacyTypeClassName // Legacy name registration if ( isNotEmpty( legacyTypeClassName ) ) { - typeReferencesByName.put( legacyTypeClassName, type ); + addTypeReference( legacyTypeClassName, type ); } // explicit registration keys @@ -429,19 +446,31 @@ private void applyRegistrationKeys(BasicTypeReference type, String[] keys) { // Incidentally, this might also help with map lookup efficiency. key = key.intern(); - // Incredibly verbose logging disabled -// LOG.tracef( "Adding type registration %s -> %s", key, type ); + addTypeReference( key, type ); + } + } + } + + private void addTypeReference(String name, BasicTypeReference typeReference) { + // Incredibly verbose logging disabled +// LOG.tracef( "Adding type registration %s -> %s", key, type ); // final BasicTypeReference old = - typeReferencesByName.put( key, type ); -// if ( old != null && old != type ) { -// LOG.tracef( -// "Type registration key [%s] overrode previous entry : `%s`", -// key, -// old -// ); -// } - } + typeReferencesByName.put( name, typeReference ); +// if ( old != null && old != type ) { +// LOG.tracef( +// "Type registration key [%s] overrode previous entry : `%s`", +// key, +// old +// ); +// } + + final var basicTypeReferences = typeReferencesByJavaTypeName.computeIfAbsent( + typeReference.getJavaType().getTypeName(), + s -> new ArrayList<>() + ); + if ( !basicTypeReferences.contains( typeReference ) ) { + basicTypeReferences.add( typeReference ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/StandardBasicTypes.java b/hibernate-core/src/main/java/org/hibernate/type/StandardBasicTypes.java index 8c16770b3caf..7966eced3ece 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/StandardBasicTypes.java +++ b/hibernate-core/src/main/java/org/hibernate/type/StandardBasicTypes.java @@ -345,13 +345,12 @@ private StandardBasicTypes() { // Date / time data /** - * The standard Hibernate type for mapping {@link Duration} to JDBC {@link org.hibernate.type.SqlTypes#INTERVAL_SECOND INTERVAL_SECOND} - * or {@link org.hibernate.type.SqlTypes#NUMERIC NUMERIC} as a fallback. + * The standard Hibernate type for mapping {@link Duration} to JDBC {@link org.hibernate.type.SqlTypes#DURATION DURATION}. */ public static final BasicTypeReference DURATION = new BasicTypeReference<>( "Duration", Duration.class, - SqlTypes.INTERVAL_SECOND + SqlTypes.DURATION ); /** diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/DurationMappingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/DurationMappingTests.java index 650fa4d87004..cd048bd123ab 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/DurationMappingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/DurationMappingTests.java @@ -16,6 +16,8 @@ import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.type.SqlTypes; import org.hibernate.type.descriptor.jdbc.AdjustableJdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType; @@ -44,6 +46,7 @@ * 2.2.21. Duration * By default, Hibernate maps Duration to the NUMERIC SQL type. */ +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsIntervalSecondType.class) @ServiceRegistry(settings = @Setting(name = AvailableSettings.PREFERRED_DURATION_JDBC_TYPE, value = "INTERVAL_SECOND")) public class DurationMappingTests { diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/basic/NationalizedTest.java b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/basic/NationalizedTest.java new file mode 100644 index 000000000000..75a3406de224 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/basic/NationalizedTest.java @@ -0,0 +1,65 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.envers.integration.basic; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import org.hibernate.annotations.Nationalized; +import org.hibernate.community.dialect.DerbyDialect; +import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.HANADialect; +import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.SybaseDialect; +import org.hibernate.envers.Audited; +import org.hibernate.mapping.Table; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.DomainModelScope; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.hibernate.type.StandardBasicTypes; +import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; + +import static org.hibernate.boot.model.naming.Identifier.toIdentifier; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@JiraKey(value = "HHH-19976") +@DomainModel(annotatedClasses = {NationalizedTest.NationalizedEntity.class}) +@SessionFactory +@SkipForDialect(dialectClass = OracleDialect.class) +@SkipForDialect(dialectClass = PostgreSQLDialect.class, matchSubTypes = true, reason = "@Lob field in HQL predicate fails with error about text = bigint") +@SkipForDialect(dialectClass = HANADialect.class, matchSubTypes = true, reason = "HANA doesn't support comparing LOBs with the = operator") +@SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "Sybase doesn't support comparing LOBs with the = operator") +@SkipForDialect(dialectClass = DB2Dialect.class, matchSubTypes = true, reason = "DB2 jdbc driver doesn't support setNString") +@SkipForDialect(dialectClass = DerbyDialect.class, matchSubTypes = true, reason = "Derby jdbc driver doesn't support setNString") +public class NationalizedTest { + + @Test + public void testMetadataBindings(DomainModelScope scope) { + final var domainModel = scope.getDomainModel(); + + assertThrows( AssertionFailedError.class, () -> { + final Table auditTable = domainModel.getEntityBinding( NationalizedEntity.class.getName() + "_AUD" ) + .getTable(); + + final org.hibernate.mapping.Column colDef = auditTable.getColumn( toIdentifier( "nationalizedString" ) ); + assertEquals( StandardBasicTypes.NSTRING.getName(), colDef.getTypeName() ); + } ); + } + + @Entity(name = "NationalizedEntity") + @Audited + public static class NationalizedEntity { + @Id + @GeneratedValue + private Integer id; + @Nationalized + private String nationalizedString; + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java index b6e415441cbd..b76ceb011792 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java @@ -1113,6 +1113,12 @@ public boolean apply(Dialect dialect) { } } + public static class SupportsIntervalSecondType implements DialectFeatureCheck { + public boolean apply(Dialect dialect) { + return definesDdlType( dialect, SqlTypes.INTERVAL_SECOND ); + } + } + public static class SupportsVectorType implements DialectFeatureCheck { public boolean apply(Dialect dialect) { return definesDdlType( dialect, SqlTypes.VECTOR ); From f24de36a4fe47c0b5f636fbd6018064c1d990fdf Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 11 Dec 2025 11:25:27 -0700 Subject: [PATCH 084/181] HHH-3192 - SchemaValidator column nullability check --- .../internal/AbstractSchemaValidator.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java index 2df34e1b52f9..25a404c62ba1 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java @@ -139,6 +139,7 @@ protected void validateTable( ); } validateColumnType( table, column, existingColumn, metadata, dialect ); + validateColumnNullability( table, column, existingColumn ); } } @@ -164,6 +165,22 @@ protected void validateColumnType( } } + private void validateColumnNullability(Table table, Column column, ColumnInformation existingColumn) { + if ( existingColumn.getNullable() == Boolean.FALSE ) { + // the existing schema column is defined as not-nullable + if ( column.isNullable() ) { + // but it is mapped in the model as nullable + throw new SchemaManagementException( + String.format( + "Schema validation: column defined as not-null in the database, but nullable in model - [%s] in table [%s]", + column.getName(), + table.getQualifiedTableName() + ) + ); + } + } + } + protected void validateSequence(Sequence sequence, SequenceInformation sequenceInformation) { if ( sequenceInformation == null ) { throw new SchemaManagementException( From 6cecc13f80a67e9ba70c9eb837c74de78f8a0d68 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 12 Dec 2025 11:22:40 -0700 Subject: [PATCH 085/181] Create 7.2 branch --- migration-guide.adoc | 147 +------------------------------------------ whats-new.adoc | 140 +---------------------------------------- 2 files changed, 3 insertions(+), 284 deletions(-) diff --git a/migration-guide.adoc b/migration-guide.adoc index 8e5c7d93d5f4..de194e897d1e 100644 --- a/migration-guide.adoc +++ b/migration-guide.adoc @@ -2,7 +2,7 @@ :toc2: :sectanchors: :toclevels: 4 -:version: 7.2 +:version: 7.3 :docsBase: https://docs.jboss.org/hibernate/orm :versionDocBase: {docsBase}/{version} :userGuideBase: {versionDocBase}/userguide/html_single/Hibernate_User_Guide.html @@ -22,11 +22,6 @@ earlier versions, see any other pertinent migration guides as well. See the link:{releaseSeriesBase}[website] for the list of requirements for the {version} series. -[[requirements-building]] -=== Building - -Starting with version 7.2, Gradle 9 and Java 25 are required to build the project. -However, the produced artifacts are still compatible with Java 17 and the produced Gradle plugin is still compatible with Gradle 8. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -48,50 +43,6 @@ See the link:{releaseSeriesBase}#whats-new[website] for the list of new features This section describes changes to contracts (classes, interfaces, methods, etc.) which are considered https://hibernate.org/community/compatibility-policy/#api[API]. -[[contains]] -=== Session.contains(String,Object) deprecated - -The method `contains(String,Object)` of `Session` was deprecated. -Use `contains(Object)` instead. - -[[noInterceptor]] -== SharedSessionBuilder.noInterceptor() - -The behavior of `SharedSessionBuilder.noInterceptor()` was changed to reflect its documented semantics. - -[[jpa]] -=== @Jpa (hibernate-testing) - -The following methods have been removed from the `@Jpa` test utility, located in the hibernate-testing project: - -- `jpaComplianceEnabled()` -- `queryComplianceEnabled()` -- `transactionComplianceEnabled()` -- `closedComplianceEnabled()` -- `orderByMappingComplianceEnabled()` -- `proxyComplianceEnabled()` -- `cacheComplianceEnabled()` -- `generatorScopeComplianceEnabled()` -- `loadByIdComplianceEnabled()` - -The default value for these settings is still `false`. - -To indicate a specific flag setting, or to override the generic value that was set with `JpaComplianceSettings.JPA_COMPLIANCE`, `@Jpa` 's `integrationSettings` array should be used. - -Example: - -``` -@Jpa(annotatedClasses = - ..., - integrationSettings = { - // set all jpa compliance flags to true - @Setting(name = JpaComplianceSettings.JPA_COMPLIANCE, value = "true"), - // override or set a specific flag value to false - @Setting(name = JpaComplianceSettings.JPA_PROXY_COMPLIANCE, value = "false") - } -) -``` - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // SPI changes @@ -102,22 +53,6 @@ Example: This section describes changes to contracts (classes, interfaces, methods, etc.) which are considered https://hibernate.org/community/compatibility-policy/#spi[SPI]. -[[registry-generic-signatures]] -=== Registry Generic Signatures - -Some operations of `TypeConfiguration`, `JavaTypeRegistry`, and `BasicTypeRegistry` had previously used unbound type parameters in the return type. The generic signatures of these methods have been changed for improved type safety. - - -[[AzureSQLServerDialect]] -=== AzureSQLServerDialect Deprecation - -`org.hibernate.dialect.AzureSQLServerDialect` was deprecated; use `org.hibernate.dialect.SQLServerDialect` instead. - -[IMPORTANT] -==== -If you set `hibernate.boot.allow_jdbc_metadata_access=false` for offline startup, -remember to also set the targeted database version through `jakarta.persistence.database-product-version` - this would be 16.0 for SQL Server 2022 or 17.0 for SQL Server 2025. -==== // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Changes in Behavior @@ -126,80 +61,8 @@ remember to also set the targeted database version through `jakarta.persistence. [[behavior-changes]] == Changes in Behavior -[[child-session-flush-close]] -=== Child Session Flush/Close Behavior +This section describes changes in behavior that applications should be aware of. -`Session` and `StatelessSession` which share transactional context with a parent now have slightly different semantics in regard to flushing and closing - - -* when the parent is flushed, the child is flushed -* when the parent is closed, the child is closed - -[NOTE] -==== -This led to a change in triggering of flush events for both - - -* `SessionEventListener` registrations -* JFR events - -In both cases, the events are now triggered regardless of whether any entities or collections were actually flushed. -Each already carried the number of entities and the number of collections which were actually flushed. -Previously, when no entities and no collections were flushed to the database no event was generated; the event is now generated and both values will be zero. - -Interestingly, this now also aligns with handling for auto-flush events which already always triggered these events. -==== - - -[[child-session-no-interceptor]] -=== Child Session No-Interceptor Behavior - -The behavior of `noInterceptor()` for `SharedSessionBuilder` and (the new) `SharedStatelessSessionBuilder` was aligned with the preexisting semantics of this method on `SessionBuilder` and `StatelessSessionBuilder`. -The previous behavior may be recovered by calling `noSessionInterceptorCreation()`. - -[[test-util-bytecodeenhanced-engine]] -=== JUnit `BytecodeEnhancedTestEngine` - -With this version, `BytecodeEnhancedTestEngine` will be disabled by default. -To be able to run the tests annotated with `@BytecodeEnhanced`, this engine has to be explicitly enabled via a system property: - -[source, properties] ----- -hibernate.testing.bytecode.enhancement.extension.engine.enabled=true ----- - -For a Maven-based project this can be done as part of the failsafe/surefire plugin configuration: - -[source, xml] ----- - - org.apache.maven.plugins - maven-surefire-plugin - - - true - - - - - - ----- - -While for a Gradle-based project this can be done in multiple ways, it essentially comes down to configuring the -link:https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:systemProperties[`systemProperties`] -of a `Test` task: - -[source, groovy] ----- -tasks.withType(Test.class) { - // Enable bytecode enhanced engine: - systemProperties.systemProperties['hibernate.testing.bytecode.enhancement.extension.engine.enabled'] = true - // Any other test task configurations - // ... -} ----- - -Also, this engine is expected to only work with the exact version of JUnit that `org.hibernate.orm:hibernate-testing` depends on. -While it may work with other versions as well, there is no guarantee of that. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // DDL changes @@ -217,9 +80,3 @@ Such changes typically do not impact programs using a relational schema managed This section describes changes to dependencies used by Hibernate ORM. -[[junit6]] -=== JUnit 6 update - -Hibernate ORM test utils distributed as `org.hibernate.orm:hibernate-testing` are now based on JUnit 6.0. -In general, this update is compatible with previous JUnit 5 versions unless the tests depended on the -<>. diff --git a/whats-new.adoc b/whats-new.adoc index 29670dd77177..f2bcd6fe6baf 100644 --- a/whats-new.adoc +++ b/whats-new.adoc @@ -2,7 +2,7 @@ :toc2: :sectanchors: :toclevels: 4 -:version: 7.2 +:version: 7.3 :docsBase: https://docs.jboss.org/hibernate/orm :versionDocBase: {docsBase}/{version} :userGuideBase: {versionDocBase}/userguide/html_single/Hibernate_User_Guide.html @@ -11,141 +11,3 @@ Describes the new features and capabilities added to Hibernate ORM in {version}. IMPORTANT: If migrating from earlier versions, be sure to also check out the link:{migrationGuide}[Migration Guide] for discussion of impactful changes. - -[[embedded-table]] -== @EmbeddedTable - -The Jakarta Persistence compliant way to specify the table to which an embedded value maps is tedious, at best, requiring us of multiple `@AttributeOverride` and/or `@AssociationOverride` annotations - - -==== -[source,java] ----- -@Entity -@Table(name="primary") -@SecondaryTable(name="secondary") -class Person { - ... - - @Embedded - @AttributeOverride(name="street", - column=@Column(table="secondary")) - @AttributeOverride(name="city", - column=@Column(table="secondary")) - @AttributeOverride(name="state", - column=@Column(table="secondary")) - @AttributeOverride(name="zip", - column=@Column(table="secondary")) - Address address; -} ----- -==== - -Hibernate now provides the `EmbeddedTable` annotation to help make this easier - - -==== -[source,java] ----- -@Entity -@Table(name="primary") -@SecondaryTable(name="secondary") -class Person { - ... - - @Embedded - @EmbeddedTable("secondary") - Address address; -} ----- -==== - -The annotation is only legal on top-level embedded. Placement on nested embedded values will be ignored. - -[[read-only-replicas]] -== Read-only replicas - -This release features experimental support for accessing data held in {versionDocBase}/introduction/html_single/#read-only-replicas[read only replicas] of the main database. - -[[FindMultipleOption]] -== Introduction of FindMultipleOption - -Previous versions of Hibernate supported loading multiple entities of a type via the `Session#byMultipleIds` method. -7.0 added `Session#findMultiple` methods which accepted `FindOption` configuration; but, for options specific to multiple-id loading, users still had to revert to `Session#byMultipleIds`. -7.2 covers this gap and introduces new `FindMultipleOption` configuration - - -* `SessionCheckMode` -* `OrderingMode` -* `RemovalsMode` - -`Session#byMultipleIds` and `MultiIdentifierLoadAccess` have been deprecated. - -[[child-stateless-sessions]] -== Child StatelessSession - -Creation of child `StatelessSession` is now supported, just as with child `Session`. -This is a `StatelessSession` which shares "transactional context" with a parent `Session` or `StatelessSession`. -Use `Session#statelessWithOptions` or `StatelessSession#statelessWithOptions` instead of `#sessionWithOptions`. - -==== -[source,java] ----- -Session parent = ...; -StatelessSession child = parent - .statelessWithOptions() - .connection() - ... - .open(); ----- -==== - -[[vector-module-enhancements]] -== Hibernate-Vector module enhancements - -Support for binary, float16 and sparse vectors were added. - -==== -[source, java, indent=0] ----- -@Entity -public class MyEntity { - @Id - UUID id; - @JdbcTypeCode(SqlTypes.VECTOR_BINARY) - @Array(length = 24) // Refers to the bit count - byte[] binaryVector; - @JdbcTypeCode(SqlTypes.VECTOR_FLOAT16) - @Array(length = 3) - float[] float16Vector; - @Array(length = 3) - SparseFloatVector sparseFloat32Vector; -} ----- -==== - -The Hibernate Vector module currently ships with 3 sparse vector types: - -* `SparseByteVector` -* `SparseFloatVector` -* `SparseDoubleVector` - -In addition to accessing the sparse indices and values, they also implement the `List` interface to provide access -as if it were a dense vector. - -Also, support for vectors in the following databases was added: - -* https://dev.mysql.com/doc/refman/9.4/en/vector-functions.html[MySQL 9.0+] -* https://www.ibm.com/docs/en/db2/12.1.0?topic=list-vector-values[DB2 12.1+] -* https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-vector-engine-guide/sap-hana-cloud-sap-hana-database-vector-engine-guide[SAP HANA Cloud QRC 4/2024+] -* https://learn.microsoft.com/en-us/sql/t-sql/data-types/vector-data-type?view=sql-server-ver17[SQL Server 2025+] - -== Enhancements to SchemaManager - -Two new methods were added to `SchemaManager`: - -- `resynchronizeGenerators()` force all sequences to catch up with data imported to their corresponding tables -- `truncateTable()` truncates a single table and resets its corresponding primary key sequence - -In addition, `truncate()` now automatically resets all primary key sequences and tables backing table generators. - -== Regular expression support in HQL - -The operators `like regexp` and `ilike regexp` were added to HQL, allowing {versionDocBase}/querylanguage/html_single/#like-predicate[patterns to be written as regular expressions]. \ No newline at end of file From bce6ff5ba8bd56068ce7d34bcb23c2f8ea3e34ff Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 19 Nov 2025 16:30:44 -0700 Subject: [PATCH 086/181] HHH-16383 - NaturalIdClass --- .../main/asciidoc/introduction/Entities.adoc | 15 + .../asciidoc/introduction/Interacting.adoc | 27 +- .../userguide/chapters/domain/natural_id.adoc | 147 +- .../src/main/java/org/hibernate/KeyType.java | 34 + .../org/hibernate/NaturalIdLoadAccess.java | 4 +- .../hibernate/NaturalIdMultiLoadAccess.java | 3 + .../hibernate/NaturalIdSynchronization.java | 19 + .../src/main/java/org/hibernate/Session.java | 2440 ++++++++--------- .../hibernate/SimpleNaturalIdLoadAccess.java | 3 + .../src/main/java/org/hibernate/Timeouts.java | 13 + .../org/hibernate/annotations/NaturalId.java | 156 +- .../hibernate/annotations/NaturalIdClass.java | 50 + .../boot/model/internal/EntityBinder.java | 54 +- .../boot/models/HibernateAnnotations.java | 4 + .../internal/NaturalIdClassAnnotation.java | 53 + .../NumberSeriesGenerateSeriesFunction.java | 6 +- .../MultiIdentifierLoadAccessImpl.java | 80 +- .../hibernate/internal/NaturalIdHelper.java | 2 +- .../NaturalIdMultiLoadAccessStandard.java | 115 +- .../org/hibernate/internal/SessionImpl.java | 220 +- .../internal/find/FindByKeyOperation.java | 338 +++ .../find/FindMultipleByKeyOperation.java | 346 +++ .../org/hibernate/internal/find/Helper.java | 31 + .../AbstractMultiNaturalIdLoader.java | 3 +- .../ast/internal/AbstractNaturalIdLoader.java | 77 +- .../CollectionBatchLoaderArrayParam.java | 4 +- .../CollectionBatchLoaderInPredicate.java | 4 +- .../internal/CollectionLoaderSingleKey.java | 4 +- .../internal/DatabaseSnapshotExecutor.java | 10 +- .../internal/EntityBatchLoaderArrayParam.java | 8 +- .../EntityBatchLoaderInPredicate.java | 8 +- .../loader/ast/internal/LoaderHelper.java | 8 +- .../ast/internal/MultiKeyLoadChunker.java | 6 +- .../MultiNaturalIdLoadingBatcher.java | 10 +- .../SingleUniqueKeyEntityLoaderStandard.java | 12 +- .../loader/ast/spi/MultiIdEntityLoader.java | 12 +- .../loader/ast/spi/MultiIdLoadOptions.java | 11 +- .../loader/ast/spi/MultiLoadOptions.java | 10 +- .../ast/spi/MultiNaturalIdLoadOptions.java | 9 +- .../loader/ast/spi/MultiNaturalIdLoader.java | 34 +- .../loader/ast/spi/NaturalIdLoader.java | 52 +- .../internal/BaseNaturalIdLoadAccessImpl.java | 9 + .../SimpleNaturalIdLoadAccessImpl.java | 15 +- .../java/org/hibernate/mapping/RootClass.java | 10 + .../metamodel/mapping/EntityMappingType.java | 15 +- .../metamodel/mapping/NaturalIdMapping.java | 136 +- .../internal/AbstractNaturalIdMapping.java | 4 +- .../internal/CompoundNaturalIdMapping.java | 322 ++- .../internal/GeneratedValuesProcessor.java | 12 +- .../internal/SimpleNaturalIdMapping.java | 16 +- .../entity/AbstractEntityPersister.java | 21 +- .../internal/MatchingIdSelectionHelper.java | 19 +- .../cte/AbstractCteMutationHandler.java | 22 +- .../internal/cte/CteInsertHandler.java | 8 +- .../inline/AbstractInlineHandler.java | 6 +- .../temptable/TableBasedInsertHandler.java | 31 +- .../sql/ast/SqlAstTranslatorFactory.java | 4 +- .../spi/StandardSqlAstTranslatorFactory.java | 4 +- .../hibernate/stat/NaturalIdStatistics.java | 7 + .../internal/NaturalIdStatisticsImpl.java | 10 + .../stat/internal/StatisticsImpl.java | 5 + .../stat/spi/StatisticsImplementor.java | 4 + .../orm/test/annotations/naturalid/A.java | 72 - .../test/annotations/naturalid/Citizen.java | 79 - .../orm/test/annotations/naturalid/D.java | 51 - .../naturalid/NaturalIdOnManyToOne.java | 47 - .../orm/test/annotations/naturalid/State.java | 41 - .../identifier/MutableNaturalIdTest.java | 53 +- .../identifier/SimpleNaturalIdTest.java | 29 + .../naturalid/ByteArrayNaturalIdTest.java | 2 +- .../naturalid/LoadByNaturalIdTest.java | 20 + .../NaturalIdAndAssociationTest.java | 2 +- .../NaturalIdOnSingleManyToOneTest.java | 2 +- .../naturalid/NaturalIdTest.java | 148 +- .../NaturalIdUniqueConstraintNameTest.java | 2 +- .../BasicNaturalIdCachingTests.java | 2 +- .../CompoundNaturalIdCacheTest.java | 2 +- .../naturalid/cid/A.java | 2 +- .../naturalid/cid/AId.java | 2 +- .../AnnotatedCompositeIdAndNaturalIdTest.java | 29 - .../cid/EmbeddedAndNaturalIdTest.java | 2 +- .../cid/HbmCompositeIdAndNaturalIdTest.java | 29 - .../AbstractCompositeIdAndNaturalIdTest.java | 26 +- .../naturalid/{cid => composite}/Account.java | 9 +- .../{cid => composite}/AccountId.java | 17 +- .../AnnotatedCompositeIdAndNaturalIdTest.java | 29 + .../CompoundNaturalIdTest.java | 2 +- .../CompoundNaturalIdTests.java | 92 +- .../HbmCompositeIdAndNaturalIdTest.java | 29 + .../CompoundNaturalIdMappingTest.java | 63 - .../mapping/naturalid/compound/Country.java | 52 - .../naturalid/compound/PostalCarrier.java | 61 - .../idclass/SimpleNaturalIdClassTests.java | 226 ++ .../ImmutableManyToOneNaturalIdHbmTest.java | 2 +- .../immutable/ImmutableNaturalIdTest.java | 32 +- .../ImmutableNaturalKeyLookupTest.java | 102 +- .../mutable/MutableNaturalIdTest.java | 4 +- .../nullable/NullableNaturalIdTest.java | 20 +- .../natural-id/composite}/Account.hbm.xml | 2 +- .../ParentChildWithManyToOne.hbm.xml | 0 .../natural-id}/immutable/User.hbm.xml | 0 .../natural-id}/mutable/User.hbm.xml | 0 .../natural-id}/nullable/User.hbm.xml | 0 .../hibernate/testing/util/ast/HqlHelper.java | 6 +- whats-new.adoc | 68 + 105 files changed, 3942 insertions(+), 2638 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/KeyType.java create mode 100644 hibernate-core/src/main/java/org/hibernate/NaturalIdSynchronization.java create mode 100644 hibernate-core/src/main/java/org/hibernate/annotations/NaturalIdClass.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/models/annotations/internal/NaturalIdClassAnnotation.java create mode 100644 hibernate-core/src/main/java/org/hibernate/internal/find/FindByKeyOperation.java create mode 100644 hibernate-core/src/main/java/org/hibernate/internal/find/FindMultipleByKeyOperation.java create mode 100644 hibernate-core/src/main/java/org/hibernate/internal/find/Helper.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/A.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/Citizen.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/D.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdOnManyToOne.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/State.java rename hibernate-core/src/test/java/org/hibernate/orm/test/{annotations => mapping}/naturalid/ByteArrayNaturalIdTest.java (97%) rename hibernate-core/src/test/java/org/hibernate/orm/test/{annotations => mapping}/naturalid/NaturalIdAndAssociationTest.java (98%) rename hibernate-core/src/test/java/org/hibernate/orm/test/{annotations => mapping}/naturalid/NaturalIdOnSingleManyToOneTest.java (98%) rename hibernate-core/src/test/java/org/hibernate/orm/test/{annotations => mapping}/naturalid/NaturalIdTest.java (82%) rename hibernate-core/src/test/java/org/hibernate/orm/test/{annotations => mapping}/naturalid/NaturalIdUniqueConstraintNameTest.java (98%) rename hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/{ => caching}/BasicNaturalIdCachingTests.java (98%) rename hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/{compound => caching}/CompoundNaturalIdCacheTest.java (98%) rename hibernate-core/src/test/java/org/hibernate/orm/test/{annotations => mapping}/naturalid/cid/A.java (91%) rename hibernate-core/src/test/java/org/hibernate/orm/test/{annotations => mapping}/naturalid/cid/AId.java (92%) delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/AnnotatedCompositeIdAndNaturalIdTest.java rename hibernate-core/src/test/java/org/hibernate/orm/test/{annotations => mapping}/naturalid/cid/EmbeddedAndNaturalIdTest.java (97%) delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/HbmCompositeIdAndNaturalIdTest.java rename hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/{cid => composite}/AbstractCompositeIdAndNaturalIdTest.java (75%) rename hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/{cid => composite}/Account.java (86%) rename hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/{cid => composite}/AccountId.java (75%) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/AnnotatedCompositeIdAndNaturalIdTest.java rename hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/{compound => composite}/CompoundNaturalIdTest.java (98%) rename hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/{ => composite}/CompoundNaturalIdTests.java (77%) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/HbmCompositeIdAndNaturalIdTest.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/CompoundNaturalIdMappingTest.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/Country.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/PostalCarrier.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/idclass/SimpleNaturalIdClassTests.java rename hibernate-core/src/test/java/org/hibernate/orm/test/{annotations/naturalid => mapping/naturalid/immutable}/ImmutableNaturalKeyLookupTest.java (79%) rename hibernate-core/src/test/resources/{org/hibernate/orm/test/mapping/naturalid/cid => mappings/natural-id/composite}/Account.hbm.xml (94%) rename hibernate-core/src/test/resources/{org/hibernate/orm/test/mapping/naturalid => mappings/natural-id}/immutable/ParentChildWithManyToOne.hbm.xml (100%) rename hibernate-core/src/test/resources/{org/hibernate/orm/test/mapping/naturalid => mappings/natural-id}/immutable/User.hbm.xml (100%) rename hibernate-core/src/test/resources/{org/hibernate/orm/test/mapping/naturalid => mappings/natural-id}/mutable/User.hbm.xml (100%) rename hibernate-core/src/test/resources/{org/hibernate/orm/test/mapping/naturalid => mappings/natural-id}/nullable/User.hbm.xml (100%) diff --git a/documentation/src/main/asciidoc/introduction/Entities.adoc b/documentation/src/main/asciidoc/introduction/Entities.adoc index 8ad97a494825..ff240136414e 100644 --- a/documentation/src/main/asciidoc/introduction/Entities.adoc +++ b/documentation/src/main/asciidoc/introduction/Entities.adoc @@ -526,6 +526,21 @@ Hibernate automatically generates a `UNIQUE` constraint on the columns mapped by Consider using the natural id attributes to implement <>. ==== +In cases where the natural id is defined by multiple attributes, Hibernate also offers the link:{doc-javadoc-url}org/hibernate/annotations/NaturalIdClass.html[`@NaturalIdClass`] annotation which acts similarly to the Jakarta Persistence `@IdClass` annotation for <> - + +[source,java] +---- +record BookKey(String isbn, int printing) {} + +@Entity +@NaturalIdClass(BookKey.class) +class Book { + ... +} +---- + +See the link:{doc-user-guide-url}#naturalid[User Guide] for more details about natural ids. + The payoff for doing this extra work, as we will see <>, is that we can take advantage of optimized natural id lookups that make use of the second-level cache. Note that even when you've identified a natural key, we still recommend the use of a generated surrogate key in foreign keys, since this makes your data model _much_ easier to change. diff --git a/documentation/src/main/asciidoc/introduction/Interacting.adoc b/documentation/src/main/asciidoc/introduction/Interacting.adoc index f6ac49148954..d78f51137bac 100644 --- a/documentation/src/main/asciidoc/introduction/Interacting.adoc +++ b/documentation/src/main/asciidoc/introduction/Interacting.adoc @@ -276,6 +276,7 @@ Modifications are automatically detected when the session is <>. On the other hand, except for `getReference()`, the following operations all result in immediate access to the database: +[[methods-for-reading]] .Methods for reading and locking data [%breakable,cols="30,~"] |=== @@ -368,9 +369,30 @@ The following code results in a single SQL `select` statement: List books = session.findMultiple(Book.class, bookIds); ---- +[[load-by-natural-id]] +As discussed <>, Hibernate offers the ability to map a natural id and perform load operations using that natural id. +This is accomplished using the `KeyType#NATURAL` `FindOption` - + +[source,java] +---- +var bookKey = new BookKey(...); +var book = session.find(Book.class, bookKey, NATURAL); +var books = session.findMultiple(Book.class, List.of(bookKey), NATURAL); +---- + +When loading by natural id, the type of value accepted depends on the type of natural id. +For single-attribute natural ids, whether defined by a basic or embedded type, the attribute type should be used. +For multi-attribute natural ids, Hibernate will accept a number of forms: + +* If a link:{doc-javadoc-url}org/hibernate/annotations/NaturalIdClass.html[`@NaturalIdClass`] is defined, an instance of the natural id class may be used. +* A `List` of the individual attribute values, ordered alphabetically by name, may be used. +* A `Map` of the individual attribute values, keyed by the attribute name, may be used. + Each of the operations we've seen so far affects a single entity instance passed as an argument. But there's a way to set things up so that an operation will propagate to associated entities. +See the link:{doc-user-guide-url}#find-by-natural-id[User Guide] for more details about loading by natural ids. + [[cascade]] === Cascading persistence operations @@ -595,7 +617,8 @@ Therefore, Hibernate has some APIs that streamline certain more complicated look [NOTE] ==== -Since the introduction of `FindOption` in JPA 3.2, `byId()` is now much less useful. +Since the introduction of `FindOption` in JPA 3.2, `byId()` is now much less useful and deprecated. +Instead, use `find()` and `findMultiple()` as discussed <>. ==== Batch loading is very useful when we need to retrieve multiple instances of the same entity class by id: @@ -627,6 +650,8 @@ We also have some operations for working with lookups by <>, Hibernate provides an efficient means for loading an entity by its natural id much like it offers for loading by identifier (PK). [[naturalid-mapping]] ==== Natural Id Mapping @@ -50,104 +43,112 @@ include::{example-dir-naturalid}/MultipleNaturalIdTest.java[tags=naturalid-multi ---- ==== -[[naturalid-api]] -==== Natural Id API - -As stated before, Hibernate provides an API for loading entities by their associated natural id. -This is represented by the `org.hibernate.NaturalIdLoadAccess` contract obtained via Session#byNaturalId. +Natural ids defined using multiple persistent attributes may also define a link:{doc-javadoc-url}org/hibernate/annotations/NaturalIdClass.html[`@NaturalIdClass`] which can be used for <>. -[NOTE] -==== -If the entity does not define a natural id, trying to load an entity by its natural id will throw an exception. -==== - -[[naturalid-load-access-example]] -.Using NaturalIdLoadAccess +[[naturalidclass-example]] +.Natural id with @NaturalIdClass ==== [source,java] ---- -include::{example-dir-naturalid}/SimpleNaturalIdTest.java[tags=naturalid-load-access-example,indent=0] +include::{core-test-base-dir}/mapping/naturalid/idclass/SimpleNaturalIdClassTests.java[tags=naturalidclass-mapping-example,indent=0] ---- +==== -[source,java] ----- -include::{example-dir-naturalid}/CompositeNaturalIdTest.java[tags=naturalid-load-access-example,indent=0] ----- +[[natural-id-mutability]] +==== Natural id mutability + +A natural id may be mutable or immutable. By default, the `@NaturalId` annotation marks the attribute as immutable. +An immutable natural id is expected to never change its value. +In fact, Hibernate will check at flush-time to ensure that the value has not been altered. + +If the value(s) of the natural id attribute(s) may change, `@NaturalId(mutable = true)` should be used instead. + +[[naturalid-mutable-mapping-example]] +.Mutable natural id mapping +==== [source,java] ---- -include::{example-dir-naturalid}/MultipleNaturalIdTest.java[tags=naturalid-load-access-example,indent=0] +include::{example-dir-naturalid}/MutableNaturalIdTest.java[tags=naturalid-mutable-mapping-example,indent=0] ---- ==== -NaturalIdLoadAccess offers 2 distinct methods for obtaining the entity: -`load()`:: obtains a reference to the entity, making sure that the entity state is initialized. -`getReference()`:: obtains a reference to the entity. The state may or may not be initialized. -If the entity is already associated with the current running Session, that reference (loaded or not) is returned. -If the entity is not loaded in the current Session and the entity supports proxy generation, an uninitialized proxy is generated and returned, otherwise the entity is loaded from the database and returned. -`NaturalIdLoadAccess` allows loading an entity by natural id and at the same time applies a pessimistic lock. -For additional details on locking, see the <> chapter. -We will discuss the last method available on NaturalIdLoadAccess ( `setSynchronizationEnabled()` ) in <>. -Because the `Book` entities in the first two examples define "simple" natural ids, we can load them as follows: -[[naturalid-simple-load-access-example]] -.Loading by simple natural id + + + + + + + + + + + +[[natural-id-caching]] +==== Natural id resolution caching + +Within the Session, Hibernate maintains a cross reference of the resolutions from natural id values to entity identifier (PK) values. +We can also have this value resolution cached in the second level cache if second level caching is enabled. + +[[naturalid-caching]] +.Natural id caching ==== [source,java] ---- -include::{example-dir-naturalid}/SimpleNaturalIdTest.java[tags=naturalid-simple-load-access-example,indent=0] +include::{example-dir-caching}/CacheableNaturalIdTest.java[tags=naturalid-cacheable-mapping-example,indent=0] ---- +==== -[source,java] ----- -include::{example-dir-naturalid}/CompositeNaturalIdTest.java[tags=naturalid-simple-load-access-example,indent=0] ----- +[IMPORTANT] +==== +Think carefully before caching resolutions for natural ids which are partially or fully <> in the second level cache as this will often have a negative impact on performance. ==== -Here we see the use of the `org.hibernate.SimpleNaturalIdLoadAccess` contract, -obtained via `Session#bySimpleNaturalId()`. -`SimpleNaturalIdLoadAccess` is similar to `NaturalIdLoadAccess` except that it does not define the using method. -Instead, because these _simple_ natural ids are defined based on just one attribute we can directly pass -the corresponding natural id attribute value directly to the `load()` and `getReference()` methods. + +[[find-by-natural-id]] +[[naturalid-api]] +==== Loading by natural id + +Hibernate provides a means to load one or more entities by natural id using the `KeyType.NATURAL` `FindOption` passed to `find()` or `findMultiple()`. [NOTE] ==== -If the entity does not define a natural id, or if the natural id is not of a "simple" type, an exception will be thrown there. +Hibernate historically offered the dedicated `byNaturalId()`, `bySimpleNaturalId()` and `byMultipleNaturalId()` APIs for loading one or more entities by natural id using its legacy "load access" approach. However, with JPA 3.2 and the introduction of `FindOption`, etc., these "load access" approaches are considered deprecated and are not discussed here. ==== -[[naturalid-mutability-caching]] -==== Natural Id - Mutability and Caching - -A natural id may be mutable or immutable. By default the `@NaturalId` annotation marks an immutable natural id attribute. -An immutable natural id is expected to never change its value. - -If the value(s) of the natural id attribute(s) change, `@NaturalId(mutable = true)` should be used instead. -[[naturalid-mutable-mapping-example]] -.Mutable natural id mapping +[[find-by-natural-id-example]] +.Loading by natural id ==== [source,java] ---- -include::{example-dir-naturalid}/MutableNaturalIdTest.java[tags=naturalid-mutable-mapping-example,indent=0] +include::{example-dir-naturalid}/SimpleNaturalIdTest.java[tags=naturalid-loading-example,indent=0] ---- ==== -Within the Session, Hibernate maintains a mapping from natural id values to entity identifiers (PK) values. -If natural ids values changed, it is possible for this mapping to become out of date until a flush occurs. +When loading by natural id, the type of value accepted depends on the definition of the natural id. -To work around this condition, Hibernate will attempt to discover any such pending changes and adjust them when the `load()` or `getReference()` methods are executed. -To be clear: this is only pertinent for mutable natural ids. +* For single-attribute natural ids, whether defined by a basic or embedded type, the attribute type should be used. +* For multi-attribute natural ids, Hibernate will accept a number of forms: + +** If a link:{doc-javadoc-url}org/hibernate/annotations/NaturalIdClass.html[`@NaturalIdClass`] is defined, an instance of the natural id class may be used. +** A `List` of the individual attribute values, ordered alphabetically by name, may be used. +** A `Map` of the individual attribute values, keyed by the attribute name, may be used. + +There are a few differences to be aware of when loading by natural id compared to loading by primary key. Most importantly, if the natural id is mutable and its values have changed, it is possible for the resolution caching to become out of date until a flush occurs resulting in incorrect results. +To work around this condition, Hibernate will attempt to discover any such pending changes and adjust them prior to performing the load. [IMPORTANT] ==== -This _discovery and adjustment_ have a performance impact. -If you are certain that none of the mutable natural ids already associated with the current `Session` have changed, you can disable this checking by calling `setSynchronizationEnabled(false)` (the default is `true`). -This will force Hibernate to circumvent the checking of mutable natural ids. +This _discovery and adjustment_ (synchronization) has a performance impact. +If you are certain that none of the mutable natural ids already associated with the current `Session` have changed, you can disable this using the `NaturalIdSynchronization.DISABLED` option which will force Hibernate to skip the checking of mutable natural ids. +To be clear: this is only pertinent for mutable natural ids. ==== [[naturalid-mutable-synchronized-example]] @@ -159,13 +160,3 @@ include::{example-dir-naturalid}/MutableNaturalIdTest.java[tags=naturalid-mutabl ---- ==== -Not only can this NaturalId-to-PK resolution be cached in the Session, but we can also have it cached in the second-level cache if second level caching is enabled. - -[[naturalid-caching]] -.Natural id caching -==== -[source,java] ----- -include::{example-dir-caching}/CacheableNaturalIdTest.java[tags=naturalid-cacheable-mapping-example,indent=0] ----- -==== diff --git a/hibernate-core/src/main/java/org/hibernate/KeyType.java b/hibernate-core/src/main/java/org/hibernate/KeyType.java new file mode 100644 index 000000000000..72c3270c6650 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/KeyType.java @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate; + +import jakarta.persistence.FindOption; + +/// FindOption allowing to load based on either id (default) or natural-id. +/// +/// @see jakarta.persistence.EntityManager#find +/// @see Session#findMultiple +/// +/// @since 7.3 +/// +/// @author Steve Ebersole +/// @author Gavin King +public enum KeyType implements FindOption { + /// Indicates to find by the entity's identifier. The default. + /// + /// @see jakarta.persistence.Id + /// @see jakarta.persistence.EmbeddedId + /// @see jakarta.persistence.IdClass + IDENTIFIER, + + /// Indicates to find based on the entity's natural-id, if one. + /// + /// @see org.hibernate.annotations.NaturalId + /// @see org.hibernate.annotations.NaturalIdClass + /// + /// @implSpec Will trigger an [IllegalArgumentException] if the entity does + /// not define a natural-id. + NATURAL +} diff --git a/hibernate-core/src/main/java/org/hibernate/NaturalIdLoadAccess.java b/hibernate-core/src/main/java/org/hibernate/NaturalIdLoadAccess.java index d60e404cfc9b..099036bc55f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/NaturalIdLoadAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/NaturalIdLoadAccess.java @@ -5,7 +5,6 @@ package org.hibernate; import jakarta.persistence.EntityGraph; - import jakarta.persistence.PessimisticLockScope; import jakarta.persistence.Timeout; import jakarta.persistence.metamodel.SingularAttribute; @@ -35,7 +34,10 @@ * @see Session#byNaturalId(Class) * @see org.hibernate.annotations.NaturalId * @see SimpleNaturalIdLoadAccess + * + * @deprecated (since 7.3) Use {@linkplain Session#findByNaturalId} instead. */ +@Deprecated public interface NaturalIdLoadAccess { /** diff --git a/hibernate-core/src/main/java/org/hibernate/NaturalIdMultiLoadAccess.java b/hibernate-core/src/main/java/org/hibernate/NaturalIdMultiLoadAccess.java index 29c21475af44..d38813665990 100644 --- a/hibernate-core/src/main/java/org/hibernate/NaturalIdMultiLoadAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/NaturalIdMultiLoadAccess.java @@ -36,7 +36,10 @@ * * @see Session#byMultipleNaturalId(Class) * @see org.hibernate.annotations.NaturalId + * + * @deprecated (since 7.3) Use {@linkplain Session#findMultipleByNaturalId} instead. */ +@Deprecated public interface NaturalIdMultiLoadAccess { /** diff --git a/hibernate-core/src/main/java/org/hibernate/NaturalIdSynchronization.java b/hibernate-core/src/main/java/org/hibernate/NaturalIdSynchronization.java new file mode 100644 index 000000000000..4aee7d7deb36 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/NaturalIdSynchronization.java @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate; + +import jakarta.persistence.FindOption; + +/// Indicates whether to perform synchronization (a sort of flush) +/// before a [find by natural-id][KeyType#NATURAL]. +/// +/// @author Steve Ebersole +public enum NaturalIdSynchronization implements FindOption { + /// Perform the synchronization. + ENABLED, + + /// Do not perform the synchronization. + DISABLED; +} diff --git a/hibernate-core/src/main/java/org/hibernate/Session.java b/hibernate-core/src/main/java/org/hibernate/Session.java index d826916f1987..32ba01c60ed4 100644 --- a/hibernate-core/src/main/java/org/hibernate/Session.java +++ b/hibernate-core/src/main/java/org/hibernate/Session.java @@ -4,1515 +4,1351 @@ */ package org.hibernate; -import java.util.Collection; -import java.util.List; -import java.util.function.Consumer; - -import jakarta.persistence.FindOption; -import jakarta.persistence.LockOption; -import jakarta.persistence.RefreshOption; -import jakarta.persistence.metamodel.EntityType; -import org.hibernate.graph.RootGraph; -import org.hibernate.jdbc.Work; -import org.hibernate.query.NativeQuery; -import org.hibernate.query.Query; -import org.hibernate.stat.SessionStatistics; - import jakarta.persistence.CacheRetrieveMode; import jakarta.persistence.CacheStoreMode; import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityManager; +import jakarta.persistence.FindOption; import jakarta.persistence.FlushModeType; import jakarta.persistence.LockModeType; +import jakarta.persistence.LockOption; +import jakarta.persistence.RefreshOption; import jakarta.persistence.TypedQueryReference; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; +import jakarta.persistence.metamodel.EntityType; +import org.hibernate.graph.RootGraph; +import org.hibernate.query.NativeQuery; +import org.hibernate.query.Query; +import org.hibernate.stat.SessionStatistics; -/** - * The main runtime interface between a Java application and Hibernate. Represents the - * notion of a persistence context, a set of managed entity instances associated - * with a logical transaction. - *

- * The lifecycle of a {@code Session} is bounded by the beginning and end of the logical - * transaction. But a long logical transaction might span several database transactions. - *

- * The primary purpose of the {@code Session} is to offer create, read, and delete - * operations for instances of mapped entity classes. An instance may be in one of three - * states with respect to a given open session: - *

    - *
  • transient: never persistent, and not associated with the {@code Session}, - *
  • persistent: currently associated with the {@code Session}, or - *
  • detached: previously persistent, but not currently associated with the - * {@code Session}. - *
- *

- * Each persistent instance has a persistent identity determined by its type - * and identifier value. There may be at most one persistent instance with a given - * persistent identity associated with a given session. A persistent identity is - * assigned when an {@linkplain #persist(Object) instance is made persistent}. - *

- * An instance of an entity class may be associated with at most one open session. - * Distinct sessions represent state with the same persistent identity using distinct - * persistent instances of the mapped entity class. - *

- * Any instance returned by {@link #get(Class, Object)}, {@link #find(Class, Object)}, - * or by a query is persistent. A persistent instance might hold references to other - * entity instances, and sometimes these references are proxied by an - * intermediate object. When an associated entity has not yet been fetched from the - * database, references to the unfetched entity are represented by uninitialized - * proxies. The state of an unfetched entity is automatically fetched from the - * database when a method of its proxy is invoked, if and only if the proxy is - * associated with an open session. Otherwise, {@link #getReference(Object)} may be - * used to trade a proxy belonging to a closed session for a new proxy associated - * with the current session. - *

- * A transient instance may be made persistent by calling {@link #persist(Object)}. - * A persistent instance may be made detached by calling {@link #detach(Object)}. - * A persistent instance may be marked for removal, and eventually made transient, - * by calling {@link #remove(Object)}. - *

- * Persistent instances are held in a managed state by the persistence context. Any - * change to the state of a persistent instance is automatically detected and eventually - * flushed to the database. This process of automatic change detection is called - * dirty checking and can be expensive in some circumstances. Dirty checking - * may be disabled by marking an entity as read-only using - * {@link #setReadOnly(Object, boolean)} or simply by {@linkplain #detach(Object) evicting} - * it from the persistence context. A session may be set to load entities as read-only - * {@linkplain #setDefaultReadOnly(boolean) by default}, or this may be controlled at the - * {@linkplain Query#setReadOnly(boolean) query level}. - *

- * The state of a transient or detached instance may be made persistent by copying it to - * a persistent instance using {@link #merge(Object)}. All older operations which moved a - * detached instance to the persistent state are now deprecated, and clients should now - * migrate to the use of {@code merge()}. - *

- * The persistent state of a managed entity may be refreshed from the database, discarding - * all modifications to the object held in memory, by calling {@link #refresh(Object)}. - *

- * From {@linkplain FlushMode time to time}, a {@linkplain #flush() flush operation} is - * triggered, and the session synchronizes state held in memory with persistent state - * held in the database by executing SQL {@code insert}, {@code update}, and {@code delete} - * statements. Note that SQL statements are often not executed synchronously by the methods - * of the {@code Session} interface. If synchronous execution of SQL is desired, the - * {@link StatelessSession} allows this. - *

- * Each managed instance has an associated {@link LockMode}. By default, the session - * obtains only {@link LockMode#READ} on an entity instance it reads from the database - * and {@link LockMode#WRITE} on an entity instance it writes to the database. This - * behavior is appropriate for programs which use optimistic locking. - *

    - *
  • A different lock level may be obtained by explicitly specifying the mode using - * {@link #find(Class,Object,LockModeType)}, {@link #find(Class,Object,FindOption...)}, - * {@link #refresh(Object,LockModeType)}, {@link #refresh(Object,RefreshOption...)}, - * or {@link org.hibernate.query.SelectionQuery#setLockMode(LockModeType)}. - *
  • The lock level of a managed instance already held by the session may be upgraded - * to a more restrictive lock level by calling {@link #lock(Object, LockMode)} or - * {@link #lock(Object, LockModeType)}. - *
- *

- * A persistence context holds hard references to all its entities and prevents them - * from being garbage collected. Therefore, a {@code Session} is a short-lived object, - * and must be discarded as soon as a logical transaction ends. In extreme cases, - * {@link #clear()} and {@link #detach(Object)} may be used to control memory usage. - * However, for processes which read many entities, a {@link StatelessSession} should - * be used. - *

- * A session might be associated with a container-managed JTA transaction, or it might be - * in control of its own resource-local database transaction. In the case of a - * resource-local transaction, the client must demarcate the beginning and end of the - * transaction using a {@link Transaction}. A typical resource-local transaction should - * use the following idiom: - *

- * Session session = factory.openSession();
- * Transaction tx = null;
- * try {
- *     tx = session.beginTransaction();
- *     //do some work
- *     ...
- *     tx.commit();
- * }
- * catch (Exception e) {
- *     if (tx!=null) tx.rollback();
- *     throw e;
- * }
- * finally {
- *     session.close();
- * }
- * 
- *

- * It's crucially important to appreciate the following restrictions and why they exist: - *

    - *
  • If the {@code Session} throws an exception, the current transaction must be rolled - * back and the session must be discarded. The internal state of the {@code Session} - * cannot be expected to be consistent with the database after the exception occurs. - *
  • At the end of a logical transaction, the session must be explicitly {@linkplain - * #close() destroyed}, so that all JDBC resources may be released. - *
  • If a transaction is rolled back, the state of the persistence context and of its - * associated entities must be assumed inconsistent with the database, and the - * session must be discarded. - *
  • A {@code Session} is never thread-safe. It contains various different sorts of - * fragile mutable state. Each thread or transaction must obtain its own dedicated - * instance from the {@link SessionFactory}. - *
- *

- * An easy way to be sure that session and transaction management is being done correctly - * is to {@linkplain SessionFactory#inTransaction(Consumer) let the factory do it}: - *

- * sessionFactory.inTransaction(session -> {
- *     //do the work
- *     ...
- * });
- * 
- *

- * A session may be used to {@linkplain #doWork(Work) execute JDBC work} using its JDBC - * connection and transaction: - *

- * session.doWork(connection -> {
- *     try ( PreparedStatement ps = connection.prepareStatement( " ... " ) ) {
- *         ps.execute();
- *     }
- * });
- * 
- *

- * A {@code Session} instance is serializable if its entities are serializable. - *

- * Every {@code Session} is a JPA {@link EntityManager}. Furthermore, when Hibernate is - * acting as the JPA persistence provider, the method {@link EntityManager#unwrap(Class)} - * may be used to obtain the underlying {@code Session}. - *

- * Hibernate, unlike JPA, allows a persistence unit where an entity class is mapped multiple - * times, with different entity names, usually to different tables. In this case, the session - * needs a way to identify the entity name of a given instance of the entity class. Therefore, - * some operations of this interface, including operations inherited from {@code EntityManager}, - * are overloaded with a form that accepts an explicit entity name along with the instance. An - * alternative solution to this problem is to provide an {@link EntityNameResolver}. - * - * @see SessionFactory - * - * @author Gavin King - * @author Steve Ebersole - */ +import java.util.Collection; +import java.util.List; +import java.util.function.Consumer; + +/// The main runtime interface between a Java application and Hibernate. Represents the +/// notion of a _persistence context_, a set of managed entity instances associated +/// with a logical transaction. +/// +/// The lifecycle of a `Session` is bounded by the beginning and end of the logical +/// transaction. But a long logical transaction might span several database transactions. +/// +/// The primary purpose of the `Session` is to offer create, read, and delete +/// operations for instances of mapped entity classes. An instance may be in one of three +/// states with respect to a given open session: +/// +/// - _transient:_ never persistent, and not associated with the `Session`, +/// - _persistent:_ currently associated with the `Session`, or +/// - _detached:_ previously persistent, but not currently associated with the +/// `Session`. +/// +/// Each persistent instance has a _persistent identity_ determined by its type +/// and identifier value. There may be at most one persistent instance with a given +/// persistent identity associated with a given session. A persistent identity is +/// assigned when an {@linkplain #persist(Object) instance is made persistent}. +/// +/// An instance of an entity class may be associated with at most one open session. +/// Distinct sessions represent state with the same persistent identity using distinct +/// persistent instances of the mapped entity class. +/// +/// Any instance returned by [#get(Class,Object)], [#find(Class,Object)], +/// or by a query is persistent. A persistent instance might hold references to other +/// entity instances, and sometimes these references are _proxied_ by an +/// intermediate object. When an associated entity has not yet been fetched from the +/// database, references to the unfetched entity are represented by uninitialized +/// proxies. The state of an unfetched entity is automatically fetched from the +/// database when a method of its proxy is invoked, if and only if the proxy is +/// associated with an open session. Otherwise, [#getReference(Object)] may be +/// used to trade a proxy belonging to a closed session for a new proxy associated +/// with the current session. +/// +/// A transient instance may be made persistent by calling [#persist(Object)]. +/// A persistent instance may be made detached by calling [#detach(Object)]. +/// A persistent instance may be marked for removal, and eventually made transient, +/// by calling [#remove(Object)]. +/// +/// Persistent instances are held in a managed state by the persistence context. Any +/// change to the state of a persistent instance is automatically detected and eventually +/// flushed to the database. This process of automatic change detection is called +/// _dirty checking_ and can be expensive in some circumstances. Dirty checking +/// may be disabled by marking an entity as read-only using +/// [#setReadOnly(Object,boolean)] or simply by [evicting][#detach(Object)] +/// it from the persistence context. A session may be set to load entities as read-only +/// [by default][#setDefaultReadOnly(boolean)], or this may be controlled at the +/// [query level][Query#setReadOnly(boolean)]. +/// +/// The state of a transient or detached instance may be made persistent by copying it to +/// a persistent instance using [#merge(Object)]. All older operations which moved a +/// detached instance to the persistent state are now deprecated, and clients should now +/// migrate to the use of `merge()`. +/// +/// The persistent state of a managed entity may be refreshed from the database, discarding +/// all modifications to the object held in memory, by calling [#refresh(Object)]. +/// +/// From {@linkplain FlushMode time to time}, a {@linkplain #flush() flush operation} is +/// triggered, and the session synchronizes state held in memory with persistent state +/// held in the database by executing SQL `insert`, `update`, and `delete` +/// statements. Note that SQL statements are often not executed synchronously by the methods +/// of the `Session` interface. If synchronous execution of SQL is desired, the +/// [StatelessSession] allows this. +/// +/// Each managed instance has an associated [LockMode]. By default, the session +/// obtains only [LockMode#READ] on an entity instance it reads from the database +/// and [LockMode#WRITE] on an entity instance it writes to the database. This +/// behavior is appropriate for programs which use optimistic locking. +/// +/// - A different lock level may be obtained by explicitly specifying the mode using +/// [#find(Class,Object,LockModeType)], [#find(Class,Object,FindOption...)], +/// [#refresh(Object,LockModeType)], [#refresh(Object,RefreshOption...)], +/// or [org.hibernate.query.SelectionQuery#setLockMode(LockModeType)]. +/// - The lock level of a managed instance already held by the session may be upgraded +/// to a more restrictive lock level by calling [#lock(Object,LockMode)] or +/// [#lock(Object,LockModeType)]. +/// +/// A persistence context holds hard references to all its entities and prevents them +/// from being garbage collected. Therefore, a `Session` is a short-lived object, +/// and must be discarded as soon as a logical transaction ends. In extreme cases, +/// [#clear()] and [#detach(Object)] may be used to control memory usage. +/// However, for processes which read many entities, a [StatelessSession] should +/// be used. +/// +/// A session might be associated with a container-managed JTA transaction, or it might be +/// in control of its own _resource-local_ database transaction. In the case of a +/// resource-local transaction, the client must demarcate the beginning and end of the +/// transaction using a [Transaction]. A typical resource-local transaction should +/// use the following idiom: +/// +/// ```java +/// try (Session session = factory.openSession()) { +/// Transaction tx = null; +/// try { +/// tx = session.beginTransaction(); +/// //do some work +/// ... +/// tx.commit(); +/// } +/// catch (Exception e) { +/// if (tx!=null) tx.rollback(); +/// throw e; +/// } +/// } +/// ``` +/// +/// It's crucially important to appreciate the following restrictions and why they exist: +/// +/// - If the `Session` throws an exception, the current transaction must be rolled +/// back and the session must be discarded. The internal state of the `Session` +/// cannot be expected to be consistent with the database after the exception occurs. +/// - At the end of a logical transaction, the session must be explicitly +/// [destroyed][#close()], so that all JDBC resources may be released. +/// - If a transaction is rolled back, the state of the persistence context and of its +/// associated entities must be assumed inconsistent with the database, and the +/// session must be discarded. +/// - A `Session` is never thread-safe. It contains various different sorts of +/// fragile mutable state. Each thread or transaction must obtain its own dedicated +/// instance from the [SessionFactory]. +/// +/// An easy way to be sure that session and transaction management is being done correctly +/// is to [let the factory do it][SessionFactory#inTransaction(Consumer)]: +/// +/// ```java +/// sessionFactory.inTransaction(session -> { +/// //do the work +/// ... +/// }); +/// ``` +/// +/// A session may be used to [execute JDBC work][#doWork] using its JDBC +/// connection and transaction: +/// +/// ```java +/// session.doWork(connection -> { +/// try ( PreparedStatement ps = connection.prepareStatement(...) ) { +/// ps.execute(); +/// } +/// }); +/// ``` +/// +/// A `Session` instance is serializable if its entities are serializable. +/// +/// Every `Session` is a JPA [EntityManager]. Furthermore, when Hibernate is +/// acting as the JPA persistence provider, the method [#unwrap(Class)] +/// may be used to obtain the underlying `Session`. +/// +/// Hibernate, unlike JPA, allows a persistence unit where an entity class is mapped multiple +/// times, with different entity names, usually to different tables. In this case, the session +/// needs a way to identify the entity name of a given instance of the entity class. Therefore, +/// some operations of this interface, including operations inherited from `EntityManager`, +/// are overloaded with a form that accepts an explicit entity name along with the instance. An +/// alternative solution to this problem is to provide an [EntityNameResolver]. +/// +/// @see SessionFactory +/// +/// @author Gavin King +/// @author Steve Ebersole public interface Session extends SharedSessionContract, EntityManager { - /** - * Force this session to flush. Must be called at the end of a unit of work, - * before the transaction is committed. Depending on the current - * {@linkplain #setHibernateFlushMode(FlushMode) flush mode}, the session might - * automatically flush when {@link Transaction#commit()} is called, and it is not - * necessary to call this method directly. - *

- * Flushing is the process of synchronizing the underlying persistent - * store with persistable state held in memory. - * - * @throws HibernateException if changes could not be synchronized with the database - */ + /// Force this session to flush. Must be called at the end of a unit of work, + /// before the transaction is committed. Depending on the current + /// [flush mode][#getHibernateFlushMode()], the session might + /// automatically flush when [Transaction#commit()] is called, and it is not + /// necessary to call this method directly. + /// + /// _Flushing_ is the process of synchronizing the underlying persistent + /// store with persistable state held in memory. + /// + /// @throws HibernateException if changes could not be synchronized with the database @Override void flush(); - /** - * Set the current {@linkplain FlushModeType JPA flush mode} for this session. - *

- * Flushing is the process of synchronizing the underlying persistent - * store with persistable state held in memory. The current flush mode determines - * when the session is automatically flushed. - * - * @param flushMode the new {@link FlushModeType} - * - * @see #setHibernateFlushMode(FlushMode) - */ + /// Set the current [JPA flush mode][FlushModeType] for this session. + /// + /// _Flushing_ is the process of synchronizing the underlying persistent + /// store with persistable state held in memory. The current flush mode determines + /// when the session is automatically flushed. + /// + /// @param flushMode the new [FlushModeType] + /// + /// @see #setHibernateFlushMode(FlushMode) @Override void setFlushMode(FlushModeType flushMode); - /** - * Set the current {@linkplain FlushMode flush mode} for this session. - *

- * Flushing is the process of synchronizing the underlying persistent - * store with persistable state held in memory. The current flush mode determines - * when the session is automatically flushed. - *

- * The {@linkplain FlushMode#AUTO default flush mode} is sometimes unnecessarily - * aggressive. For a logically "read only" session, it's reasonable to set the - * session's flush mode to {@link FlushMode#MANUAL} at the start of the session - * in order to avoid some unnecessary work. - *

- * Note that {@link FlushMode} defines more options than {@link FlushModeType}. - * - * @param flushMode the new {@link FlushMode} - */ + /// Set the current [flush mode][FlushMode] for this session. + /// + /// _Flushing_ is the process of synchronizing the underlying persistent + /// store with persistable state held in memory. The current flush mode determines + /// when the session is automatically flushed. + /// + /// The [default flush mode][FlushMode#AUTO] is sometimes unnecessarily + /// aggressive. For a logically "read only" session, it's reasonable to set the + /// session's flush mode to [FlushMode#MANUAL] at the start of the session + /// in order to avoid some unnecessary work. + /// + /// Note that [FlushMode] defines more options than [FlushModeType]. + /// + /// @param flushMode the new [FlushMode] void setHibernateFlushMode(FlushMode flushMode); - /** - * Get the current {@linkplain FlushModeType JPA flush mode} for this session. - * - * @return the {@link FlushModeType} currently in effect - * - * @see #getHibernateFlushMode() - */ + /// Get the current [JPA flush mode][FlushModeType] for this session. + /// + /// @return the [FlushModeType] currently in effect + /// + /// @see #getHibernateFlushMode() @Override FlushModeType getFlushMode(); - /** - * Get the current {@linkplain FlushMode flush mode} for this session. - * - * @return the {@link FlushMode} currently in effect - */ + /// Get the current [flush mode][FlushMode] for this session. + /// + /// @return the [FlushMode] currently in effect FlushMode getHibernateFlushMode(); - /** - * Set the current {@linkplain CacheMode cache mode} for this session. - *

- * The cache mode determines the manner in which this session can interact with - * the second level cache. - * - * @param cacheMode the new cache mode - */ + /// Set the current [cache mode][CacheMode] for this session. + /// + /// The cache mode determines the manner in which this session can interact with + /// the second level cache. + /// + /// @param cacheMode the new cache mode void setCacheMode(CacheMode cacheMode); - /** - * Get the current {@linkplain CacheMode cache mode} for this session. - * - * @return the current cache mode - */ + /// Get the current [cache mode][CacheMode] for this session. + /// + /// @return the current cache mode CacheMode getCacheMode(); - /** - * The JPA-defined {@link CacheStoreMode}. - * - * @see #getCacheMode() - * - * @since 6.2 - */ + /// The JPA-defined [CacheStoreMode]. + /// + /// @see #getCacheMode() + /// + /// @since 6.2 @Override CacheStoreMode getCacheStoreMode(); - /** - * The JPA-defined {@link CacheRetrieveMode}. - * - * @see #getCacheMode() - * - * @since 6.2 - */ + /// The JPA-defined [CacheRetrieveMode]. + /// + /// @see #getCacheMode() + /// + /// @since 6.2 @Override CacheRetrieveMode getCacheRetrieveMode(); - /** - * Enable or disable writes to the second-level cache. - * - * @param cacheStoreMode a JPA-defined {@link CacheStoreMode} - * - * @see #setCacheMode(CacheMode) - * - * @since 6.2 - */ + /// Enable or disable writes to the second-level cache. + /// + /// @param cacheStoreMode a JPA-defined [CacheStoreMode] + /// + /// @see #setCacheMode(CacheMode) + /// + /// @since 6.2 @Override void setCacheStoreMode(CacheStoreMode cacheStoreMode); - /** - * Enable or disable reads from the second-level cache. - * - * @param cacheRetrieveMode a JPA-defined {@link CacheRetrieveMode} - * - * @see #setCacheMode(CacheMode) - * - * @since 6.2 - */ + /// Enable or disable reads from the second-level cache. + /// + /// @param cacheRetrieveMode a JPA-defined [CacheRetrieveMode] + /// + /// @see #setCacheMode(CacheMode) + /// + /// @since 6.2 @Override void setCacheRetrieveMode(CacheRetrieveMode cacheRetrieveMode); - /** - * Get the maximum batch size for batch fetching associations by - * id in this session. - * - * @since 6.3 - */ + /// Get the maximum batch size for batch fetching associations by + /// id in this session. + /// + /// @since 6.3 int getFetchBatchSize(); - /** - * Set the maximum batch size for batch fetching associations by - * id in this session. Override the - * {@linkplain org.hibernate.boot.spi.SessionFactoryOptions#getDefaultBatchFetchSize() - * factory-level} default controlled by the configuration property - * {@value org.hibernate.cfg.AvailableSettings#DEFAULT_BATCH_FETCH_SIZE}. - *

- *

    - *
  • If {@code batchSize>1}, then batch fetching is enabled. - *
  • If {@code batchSize<0}, the batch size is inherited from - * the factory-level setting. - *
  • Otherwise, batch fetching is disabled. - *
- * - * @param batchSize the maximum batch size for batch fetching - * - * @since 6.3 - * - * @see org.hibernate.cfg.AvailableSettings#DEFAULT_BATCH_FETCH_SIZE - */ + /// Set the maximum batch size for batch fetching associations by + /// id in this session. Override the + /// [factory-level][org.hibernate.boot.spi.SessionFactoryOptions#getDefaultBatchFetchSize()] + /// default controlled by the configuration property + /// {@value org.hibernate.cfg.AvailableSettings#DEFAULT_BATCH_FETCH_SIZE}. + /// + /// - If `batchSize>1`, then batch fetching is enabled. + /// - If `batchSize<0`, the batch size is inherited from + /// the factory-level setting. + /// - Otherwise, batch fetching is disabled. + /// + /// @param batchSize the maximum batch size for batch fetching + /// + /// @since 6.3 + /// + /// @see org.hibernate.cfg.AvailableSettings#DEFAULT_BATCH_FETCH_SIZE void setFetchBatchSize(int batchSize); - /** - * Determine if subselect fetching is enabled in this session. - * - * @return {@code true} is subselect fetching is enabled - * - * @since 6.3 - */ + /// Determine if subselect fetching is enabled in this session. + /// + /// @return `true` is subselect fetching is enabled + /// + /// @since 6.3 boolean isSubselectFetchingEnabled(); - /** - * Enable or disable subselect fetching in this session. Override the - * {@linkplain org.hibernate.boot.spi.SessionFactoryOptions#isSubselectFetchEnabled() - * factory-level} default controlled by the configuration property - * {@value org.hibernate.cfg.AvailableSettings#USE_SUBSELECT_FETCH}. - * - * @param enabled {@code true} to enable subselect fetching - * - * @since 6.3 - * - * @see org.hibernate.cfg.AvailableSettings#USE_SUBSELECT_FETCH - */ + /// Enable or disable subselect fetching in this session. Override the + /// [factory-level][org.hibernate.boot.spi.SessionFactoryOptions#isSubselectFetchEnabled()] + /// default controlled by the configuration property + /// {@value org.hibernate.cfg.AvailableSettings#USE_SUBSELECT_FETCH}. + /// + /// @param enabled `true` to enable subselect fetching + /// + /// @since 6.3 + /// + /// @see org.hibernate.cfg.AvailableSettings#USE_SUBSELECT_FETCH void setSubselectFetchingEnabled(boolean enabled); - /** - * Get the session factory which created this session. - * - * @return the session factory - * - * @see SessionFactory - */ + /// Get the session factory which created this session. + /// + /// @return the session factory + /// + /// @see SessionFactory SessionFactory getSessionFactory(); - /** - * Cancel the execution of the current query. - *

- * This is the sole method on session which may be safely called from - * another thread. - * - * @throws HibernateException if there was a problem cancelling the query - */ + /// Cancel the execution of the current query. + /// + /// This is the sole method on session which may be safely called from + /// another thread. + /// + /// @throws HibernateException if there was a problem cancelling the query void cancelQuery(); - /** - * Does this session contain any changes which must be synchronized with - * the database? In other words, would any DML operations be executed if - * we flushed this session? - * - * @return {@code true} if the session contains pending changes; - * {@code false} otherwise. - */ + /// Whether this session contains any changes which must be synchronized with + /// the database. In other words, would any DML operations be executed if + /// we flushed this session? + /// + /// @return `true` if the session contains pending changes; `false` otherwise. boolean isDirty(); - /** - * Will entities and proxies that are loaded into this session be made - * read-only by default? - *

- * To determine the read-only/modifiable setting for a particular entity - * or proxy use {@link #isReadOnly(Object)}. - * - * @see #isReadOnly(Object) - * - * @return {@code true}, loaded entities/proxies will be made read-only by default; - * {@code false}, loaded entities/proxies will be made modifiable by default. - */ + /// Will entities and proxies that are loaded into this session be made + /// read-only by default? + /// + /// To determine the read-only/modifiable setting for a particular entity + /// or proxy use [#isReadOnly(Object)]. + /// + /// @see #isReadOnly(Object) + /// + /// @return `true`, loaded entities/proxies will be made read-only by default; + /// `false`, loaded entities/proxies will be made modifiable by default. boolean isDefaultReadOnly(); - /** - * Change the default for entities and proxies loaded into this session - * from modifiable to read-only mode, or from read-only to modifiable mode. - *

- * Read-only entities are not dirty-checked, and snapshots of persistent - * state are not maintained. Read-only entities can be modified, but a - * modification to a field of a read-only entity is not made persistent. - *

- * When a proxy is initialized, the loaded entity will have the same - * read-only/modifiable setting as the uninitialized proxy, regardless of - * the {@linkplain #isDefaultReadOnly current default read-only mode} - * of the session. - *

- * To change the read-only/modifiable setting for a particular entity - * or proxy that already belongs to this session, use - * {@link #setReadOnly(Object, boolean)}. - *

- * To override the default read-only mode of the current session for - * all entities and proxies returned by a given {@code Query}, use - * {@link Query#setReadOnly(boolean)}. - *

- * Every instance of an {@linkplain org.hibernate.annotations.Immutable - * immutable} entity is loaded in read-only mode. - * - * @see #setReadOnly(Object,boolean) - * @see Query#setReadOnly(boolean) - * - * @param readOnly {@code true}, the default for loaded entities/proxies is read-only; - * {@code false}, the default for loaded entities/proxies is modifiable - * @throws SessionException if the session was originally - * {@linkplain SessionBuilder#readOnly created in read-only mode} - */ + /// Change the default for entities and proxies loaded into this session + /// from modifiable to read-only mode, or from read-only to modifiable mode. + /// + /// Read-only entities are not dirty-checked, and snapshots of persistent + /// state are not maintained. Read-only entities can be modified, but a + /// modification to a field of a read-only entity is not made persistent. + /// + /// When a proxy is initialized, the loaded entity will have the same + /// read-only/modifiable setting as the uninitialized proxy, regardless of + /// the [current default read-only mode][#isDefaultReadOnly] + /// of the session. + /// + /// To change the read-only/modifiable setting for a particular entity + /// or proxy that already belongs to this session, use + /// [#setReadOnly(Object,boolean)]. + /// + /// To override the default read-only mode of the current session for + /// all entities and proxies returned by a given `Query`, use + /// [Query#setReadOnly(boolean)]. + /// + /// Every instance of an [immutable][org.hibernate.annotations.Immutable] + /// entity is loaded in read-only mode. + /// + /// @see #setReadOnly(Object,boolean) + /// @see Query#setReadOnly(boolean) + /// + /// @param readOnly `true`, the default for loaded entities/proxies is read-only; + /// `false`, the default for loaded entities/proxies is modifiable + /// @throws SessionException if the session was originally + /// {@linkplain SessionBuilder#readOnly created in read-only mode} void setDefaultReadOnly(boolean readOnly); - /** - * Return the identifier value of the given entity associated with this session. - * An exception is thrown if the given entity instance is transient or detached - * in relation to this session. - * - * @param object a persistent instance associated with this session - * - * @return the identifier - * - * @throws TransientObjectException if the instance is transient or associated with - * a different session - */ + /// Return the identifier value of the given entity associated with this session. + /// An exception is thrown if the given entity instance is transient or detached + /// in relation to this session. + /// + /// @param object a persistent instance associated with this session + /// + /// @return the identifier + /// + /// @throws TransientObjectException if the instance is transient or associated with + /// a different session Object getIdentifier(Object object); - /** - * Determine if the given entity is associated with this session. - * - * @param entityName the entity name - * @param object an instance of a persistent class - * - * @return {@code true} if the given instance is associated with this {@code Session} - * - * @deprecated Use {@link #contains(Object)} instead. - */ + /// Determine if the given entity is associated with this session. + /// + /// @param entityName the entity name + /// @param object an instance of a persistent class + /// + /// @return `true` if the given instance is associated with this `Session` + /// + /// @deprecated Use [#contains(Object)] instead. @Deprecated(since = "7.2", forRemoval = true) boolean contains(String entityName, Object object); - /** - * Remove this instance from the session cache. Changes to the instance will - * not be synchronized with the database. This operation cascades to associated - * instances if the association is mapped with - * {@link jakarta.persistence.CascadeType#DETACH}. - * - * @param object the managed instance to detach - */ + /// Remove this instance from the session cache. Changes to the instance will + /// not be synchronized with the database. This operation cascades to associated + /// instances if the association is mapped with [jakarta.persistence.CascadeType#DETACH]. + /// + /// @param object the managed instance to detach @Override void detach(Object object); - /** - * Remove this instance from the session cache. Changes to the instance will - * not be synchronized with the database. This operation cascades to associated - * instances if the association is mapped with - * {@link jakarta.persistence.CascadeType#DETACH}. - *

- * This operation is a synonym for {@link #detach(Object)}. - * - * @param object the managed entity to evict - * - * @throws IllegalArgumentException if the given object is not an entity - */ + /// Remove this instance from the session cache. Changes to the instance will + /// not be synchronized with the database. This operation cascades to associated + /// instances if the association is mapped with [jakarta.persistence.CascadeType#DETACH]. + /// + /// This operation is a synonym for [#detach(Object)]. + /// + /// @param object the managed entity to evict + /// + /// @throws IllegalArgumentException if the given object is not an entity void evict(Object object); - /** - * Return the persistent instance of the given entity class with the given identifier, - * or null if there is no such persistent instance. If the instance is already associated - * with the session, return that instance. This method never returns an uninitialized - * instance. - *

- * The object returned by {@code get()} or {@code find()} is either an unproxied instance - * of the given entity class, or a fully-fetched proxy object. - *

- * This operation requests {@link LockMode#NONE}, that is, no lock, allowing the object - * to be retrieved from the cache without the cost of database access. However, if it is - * necessary to read the state from the database, the object will be returned with the - * lock mode {@link LockMode#READ}. - *

- * To bypass the {@linkplain Cache second-level cache}, and ensure that the state of the - * requested instance is read directly from the database, either: - *

    - *
  • call {@link #find(Class, Object, FindOption...)}, passing - * {@link CacheRetrieveMode#BYPASS} as an option, - *
  • call {@link #find(Class, Object, FindOption...)} with the explicit lock mode - * {@link LockMode#READ}, or - *
  • {@linkplain #setCacheRetrieveMode set the cache mode} to - * {@link CacheRetrieveMode#BYPASS} before calling this method. - *
- * - * @apiNote This operation is very similar to {@link #get(Class, Object)}. - * - * @param entityType the entity type - * @param id an identifier - * - * @return a fully-fetched persistent instance or null - */ + /// Return the persistent instance of the given entity class with the given identifier, + /// or null if there is no such persistent instance. If the instance is already associated + /// with the session, return that instance. This method never returns an uninitialized + /// instance. + /// + /// The object returned by `get()` or `find()` is either an unproxied instance + /// of the given entity class, or a fully-fetched proxy object. + /// + /// This operation requests [LockMode#NONE], that is, no lock, allowing the object + /// to be retrieved from the cache without the cost of database access. However, if it is + /// necessary to read the state from the database, the object will be returned with the + /// lock mode [LockMode#READ]. + /// + /// To bypass the [second-level cache][Cache], and ensure that the state of the + /// requested instance is read directly from the database, either: + /// + /// - call [#find(Class,Object,FindOption...)], passing + /// [CacheRetrieveMode#BYPASS] as an option, + /// - call [#find(Class,Object,FindOption...)] with the explicit lock mode + /// [LockMode#READ], or + /// - {@linkplain #setCacheRetrieveMode set the cache mode} to + /// [CacheRetrieveMode#BYPASS] before calling this method. + /// + /// @apiNote This operation is very similar to [#get(Class,Object)]. + /// + /// @param entityType the entity type + /// @param id an identifier + /// + /// @return a fully-fetched persistent instance or null @Override T find(Class entityType, Object id); - /** - * Return the persistent instance of the named entity type with the given identifier, - * or null if there is no such persistent instance. - *

- * Differs from {@linkplain #find(Class, Object)} in that this form accepts - * the entity name of a {@linkplain org.hibernate.metamodel.RepresentationMode#MAP dynamic entity}. - * - * @see #find(Class, Object) - */ + /// {@inheritDoc} + /// + /// @implNote Note that Hibernate's implementation of this method can + /// also be used for loading an entity by its [natural-id][org.hibernate.annotations.NaturalId] + /// by passing [KeyType#NATURAL] as a [FindOption] and the natural-id value as the `key` to load. + @Override + T find(Class entityClass, Object key, FindOption... options); + + /// Return the persistent instance of the named entity type with the given identifier, + /// or null if there is no such persistent instance. + /// + /// Differs from {@linkplain #find(Class, Object)} in that this form accepts + /// the entity name of a [dynamic entity][org.hibernate.metamodel.RepresentationMode#MAP]. + /// + /// @see #find(Class, Object) Object find(String entityName, Object primaryKey); - /** - * Return the persistent instance of the named entity type with the given identifier - * using the specified options, or null if there is no such persistent instance. - *

- * Differs from {@linkplain #find(Class, Object, FindOption...)} in that this form accepts - * the entity name of a {@linkplain org.hibernate.metamodel.RepresentationMode#MAP dynamic entity}. - * - * @see #find(Class, Object, FindOption...) - */ + /// Return the persistent instance of the named entity type with the given identifier + /// using the specified options, or null if there is no such persistent instance. + /// + /// Differs from [#find(Class, Object, FindOption...)] in that this form accepts + /// the entity name of a [dynamic entity][org.hibernate.metamodel.RepresentationMode#MAP]. + /// + /// @see #find(Class, Object, FindOption...) Object find(String entityName, Object primaryKey, FindOption... options); - /** - * Return the persistent instances of the given entity class with the given identifiers - * as a list. The position of an instance in the returned list matches the position of its - * identifier in the given list of identifiers, and the returned list contains a null value - * if there is no persistent instance matching a given identifier. If an instance is already - * associated with the session, that instance is returned. This method never returns an - * uninitialized instance. - *

- * Every object returned by {@code findMultiple()} is either an unproxied instance of the - * given entity class, or a fully-fetched proxy object. - *

- * This method accepts {@link BatchSize} as an option, allowing control over the number of - * records retrieved in a single database request. The performance impact of setting a batch - * size depends on whether a SQL array may be used to pass the list of identifiers to the - * database: - *

    - *
  • for databases which {@linkplain org.hibernate.dialect.Dialect#supportsStandardArrays - * support standard SQL arrays}, a smaller batch size might be extremely inefficient - * compared to a very large batch size or no batching at all, but - *
  • on the other hand, for databases with no SQL array type, a large batch size results - * in long SQL statements with many JDBC parameters. - *
- * - * @param entityType the entity type - * @param ids the list of identifiers - * @param options options, if any - * - * @return an ordered list of persistent instances, with null elements representing missing - * entities, whose positions in the list match the positions of their ids in the - * given list of identifiers - * - * @see FindMultipleOption - * - * @since 7.0 - */ + /// Return the persistent instances of the given entity class with the given identifiers + /// as a list. The position of an instance in the returned list matches the position of its + /// identifier in the given list of identifiers, and the returned list contains a null value + /// if there is no persistent instance matching a given identifier. If an instance is already + /// associated with the session, that instance is returned. This method never returns an + /// uninitialized instance. + /// + /// Every object returned by `findMultiple()` is either an unproxied instance of the + /// given entity class, or a fully-fetched proxy object. + /// + /// This method accepts [BatchSize] as an option, allowing control over the number of + /// records retrieved in a single database request. The performance impact of setting a batch + /// size depends on whether a SQL array may be used to pass the list of identifiers to the + /// database: + /// + /// - for databases which [support standard SQL arrays][org.hibernate.dialect.Dialect#supportsStandardArrays], + /// a smaller batch size might be extremely inefficient compared to a very large batch size or + /// no batching at all, but + /// - on the other hand, for databases with no SQL array type, a large batch size results + /// in long SQL statements with many JDBC parameters. + /// + /// @param entityType the entity type + /// @param ids the list of identifiers + /// @param options options, if any + /// + /// @return an ordered list of persistent instances, with null elements representing missing + /// entities, whose positions in the list match the positions of their ids in the + /// given list of identifiers + /// + /// @see FindMultipleOption + /// + /// @since 7.0 List findMultiple(Class entityType, List ids, FindOption... options); - /** - * Return the persistent instances of the root entity of the given {@link EntityGraph} - * with the given identifiers as a list, fetching the associations specified by the - * graph, which is interpreted as a {@linkplain org.hibernate.graph.GraphSemantic#LOAD - * load graph}. The position of an instance in the returned list matches the position of - * its identifier in the given list of identifiers, and the returned list contains a null - * value if there is no persistent instance matching a given identifier. If an instance - * is already associated with the session, that instance is returned. This method never - * returns an uninitialized instance. - *

- * Every object returned by {@code findMultiple()} is either an unproxied instance of the - * given entity class, or a fully-fetched proxy object. - *

- * This method accepts {@link BatchSize} as an option, allowing control over the number of - * records retrieved in a single database request. The performance impact of setting a batch - * size depends on whether a SQL array may be used to pass the list of identifiers to the - * database: - *

    - *
  • for databases which {@linkplain org.hibernate.dialect.Dialect#supportsStandardArrays - * support standard SQL arrays}, a smaller batch size might be extremely inefficient - * compared to a very large batch size or no batching at all, but - *
  • on the other hand, for databases with no SQL array type, a large batch size results - * in long SQL statements with many JDBC parameters. - *
- * - * @param entityGraph the entity graph interpreted as a load graph - * @param ids the list of identifiers - * @param options options, if any - * - * @return an ordered list of persistent instances, with null elements representing missing - * entities, whose positions in the list match the positions of their ids in the - * given list of identifiers - * - * @see FindMultipleOption - * - * @since 7.0 - */ + /// Return the persistent instances of the root entity of the given [EntityGraph] + /// with the given identifiers as a list, fetching the associations specified by the + /// graph, which is interpreted as a [load graph][org.hibernate.graph.GraphSemantic#LOAD]. + /// The position of an instance in the returned list matches the position of + /// its identifier in the given list of identifiers, and the returned list contains a null + /// value if there is no persistent instance matching a given identifier. If an instance + /// is already associated with the session, that instance is returned. This method never + /// returns an uninitialized instance. + /// + /// Every object returned by `findMultiple()` is either an unproxied instance of the + /// given entity class, or a fully-fetched proxy object. + /// + /// This method accepts [BatchSize] as an option, allowing control over the number of + /// records retrieved in a single database request. The performance impact of setting a batch + /// size depends on whether a SQL array may be used to pass the list of identifiers to the + /// database: + /// + /// - for databases which [support standard SQL arrays][org.hibernate.dialect.Dialect#supportsStandardArrays] + /// a smaller batch size might be extremely inefficient + /// compared to a very large batch size or no batching at all, but + /// - on the other hand, for databases with no SQL array type, a large batch size results + /// in long SQL statements with many JDBC parameters. + /// + /// @param entityGraph the entity graph interpreted as a load graph + /// @param ids the list of identifiers + /// @param options options, if any + /// + /// @return an ordered list of persistent instances, with null elements representing missing + /// entities, whose positions in the list match the positions of their ids in the + /// given list of identifiers + /// + /// @see FindMultipleOption + /// + /// @since 7.0 List findMultiple(EntityGraph entityGraph, List ids, FindOption... options); - /** - * Read the persistent state associated with the given identifier into the given - * transient instance. - * - * @param object a transient instance of an entity class - * @param id an identifier - */ + /// Read the persistent state associated with the given identifier into the given + /// transient instance. + /// + /// @param object a transient instance of an entity class + /// @param id an identifier void load(Object object, Object id); - /** - * Persist the state of the given detached instance, reusing the current - * identifier value. This operation cascades to associated instances if - * the association is mapped with - * {@link org.hibernate.annotations.CascadeType#REPLICATE}. - * - * @param object a detached instance of a persistent class - * @param replicationMode the replication mode to use - * - * @deprecated With no real replacement. For some use cases try - * {@link StatelessSession#upsert(Object)}. - */ + /// Persist the state of the given detached instance, reusing the current + /// identifier value. This operation cascades to associated instances if + /// the association is mapped with [org.hibernate.annotations.CascadeType#REPLICATE]. + /// + /// @param object a detached instance of a persistent class + /// @param replicationMode the replication mode to use + /// + /// @deprecated With no real replacement. For some use cases try [StatelessSession#upsert(Object)]. @Deprecated( since = "6.0" ) void replicate(Object object, ReplicationMode replicationMode); - /** - * Persist the state of the given detached instance, reusing the current - * identifier value. This operation cascades to associated instances if - * the association is mapped with - * {@link org.hibernate.annotations.CascadeType#REPLICATE}. - * - * @param entityName the entity name - * @param object a detached instance of a persistent class - * @param replicationMode the replication mode to use - * - * @deprecated With no real replacement. For some use cases try - * {@link StatelessSession#upsert(Object)}. - */ + /// Persist the state of the given detached instance, reusing the current + /// identifier value. This operation cascades to associated instances if + /// the association is mapped with [org.hibernate.annotations.CascadeType#REPLICATE]. + /// + /// @param entityName the entity name + /// @param object a detached instance of a persistent class + /// @param replicationMode the replication mode to use + /// + /// @deprecated With no real replacement. For some use cases try [StatelessSession#upsert(Object)]. @Deprecated( since = "6.0" ) void replicate(String entityName, Object object, ReplicationMode replicationMode) ; - /** - * Copy the state of the given object onto the persistent object with the same - * identifier. If there is no persistent instance currently associated with - * the session, it will be loaded. Return the persistent instance. If the - * given instance is unsaved, save a copy and return it as a newly persistent - * instance. The given instance does not become associated with the session. - * This operation cascades to associated instances if the association is mapped - * with {@link jakarta.persistence.CascadeType#MERGE}. - * - * @param object a detached instance with state to be copied - * - * @return an updated persistent instance - */ + /// Copy the state of the given object onto the persistent object with the same + /// identifier. If there is no persistent instance currently associated with + /// the session, it will be loaded. Return the persistent instance. If the + /// given instance is unsaved, save a copy and return it as a newly persistent + /// instance. The given instance does not become associated with the session. + /// This operation cascades to associated instances if the association is mapped + /// with [jakarta.persistence.CascadeType#MERGE]. + /// + /// @param object a detached instance with state to be copied + /// + /// @return an updated persistent instance @Override T merge(T object); - /** - * Copy the state of the given object onto the persistent object with the same - * identifier. If there is no persistent instance currently associated with - * the session, it will be loaded. Return the persistent instance. If the - * given instance is unsaved, save a copy and return it as a newly persistent - * instance. The given instance does not become associated with the session. - * This operation cascades to associated instances if the association is mapped - * with {@link jakarta.persistence.CascadeType#MERGE}. - * - * @param entityName the entity name - * @param object a detached instance with state to be copied - * - * @return an updated persistent instance - */ + /// Copy the state of the given object onto the persistent object with the same + /// identifier. If there is no persistent instance currently associated with + /// the session, it will be loaded. Return the persistent instance. If the + /// given instance is unsaved, save a copy and return it as a newly persistent + /// instance. The given instance does not become associated with the session. + /// This operation cascades to associated instances if the association is mapped + /// with [jakarta.persistence.CascadeType#MERGE]. + /// + /// @param entityName the entity name + /// @param object a detached instance with state to be copied + /// + /// @return an updated persistent instance T merge(String entityName, T object); - /** - * Copy the state of the given object onto the persistent object with the same - * identifier. If there is no persistent instance currently associated with - * the session, it is loaded using the given {@link EntityGraph}, which is - * interpreted as a load graph. Return the persistent instance. If the given - * instance is unsaved, save a copy and return it as a newly persistent instance. - * The given instance does not become associated with the session. This operation - * cascades to associated instances if the association is mapped with - * {@link jakarta.persistence.CascadeType#MERGE}. - * - * @param object a detached instance with state to be copied - * @param loadGraph an entity graph interpreted as a load graph - * - * @return an updated persistent instance - * - * @since 7.0 - */ + /// Copy the state of the given object onto the persistent object with the same + /// identifier. If there is no persistent instance currently associated with + /// the session, it is loaded using the given [EntityGraph], which is + /// interpreted as a load graph. Return the persistent instance. If the given + /// instance is unsaved, save a copy and return it as a newly persistent instance. + /// The given instance does not become associated with the session. This operation + /// cascades to associated instances if the association is mapped with + /// [jakarta.persistence.CascadeType#MERGE]. + /// + /// @param object a detached instance with state to be copied + /// @param loadGraph an entity graph interpreted as a load graph + /// + /// @return an updated persistent instance + /// + /// @since 7.0 T merge(T object, EntityGraph loadGraph); - /** - * Make a transient instance persistent and mark it for later insertion in the - * database. This operation cascades to associated instances if the association - * is mapped with {@link jakarta.persistence.CascadeType#PERSIST}. - *

- * For an entity with a {@linkplain jakarta.persistence.GeneratedValue generated} - * id, {@code persist()} ultimately results in generation of an identifier for - * the given instance. But this may happen asynchronously, when the session is - * {@linkplain #flush() flushed}, depending on the identifier generation strategy. - * - * @param object a transient instance to be made persistent - */ + /// Make a transient instance persistent and mark it for later insertion in the + /// database. This operation cascades to associated instances if the association + /// is mapped with [jakarta.persistence.CascadeType#PERSIST]. + /// + /// For entities with a [generated id][jakarta.persistence.GeneratedValue], + /// `persist()` ultimately results in generation of an identifier for the + /// given instance. But this may happen asynchronously, when the session is + /// [flushed][#flush()], depending on the identifier generation strategy. + /// + /// @param object a transient instance to be made persistent @Override void persist(Object object); - /** - * Make a transient instance persistent and mark it for later insertion in the - * database. This operation cascades to associated instances if the association - * is mapped with {@link jakarta.persistence.CascadeType#PERSIST}. - *

- * For entities with a {@link jakarta.persistence.GeneratedValue generated id}, - * {@code persist()} ultimately results in generation of an identifier for the - * given instance. But this may happen asynchronously, when the session is - * {@linkplain #flush() flushed}, depending on the identifier generation strategy. - * - * @param entityName the entity name - * @param object a transient instance to be made persistent - */ + /// Make a transient instance persistent and mark it for later insertion in the + /// database. This operation cascades to associated instances if the association + /// is mapped with [jakarta.persistence.CascadeType#PERSIST]. + /// + /// For entities with a [generated id][jakarta.persistence.GeneratedValue], + /// `persist()` ultimately results in generation of an identifier for the + /// given instance. But this may happen asynchronously, when the session is + /// [flushed][#flush()], depending on the identifier generation strategy. + /// + /// @param entityName the entity name + /// @param object a transient instance to be made persistent void persist(String entityName, Object object); - /** - * Obtain the specified lock level on the given managed instance associated - * with this session. This operation may be used to: - *

    - *
  • perform a version check on an entity read from the second-level cache - * by requesting {@link LockMode#READ}, - *
  • schedule a version check at transaction commit by requesting - * {@link LockMode#OPTIMISTIC}, - *
  • schedule a version increment at transaction commit by requesting - * {@link LockMode#OPTIMISTIC_FORCE_INCREMENT} - *
  • upgrade to a pessimistic lock with {@link LockMode#PESSIMISTIC_READ} - * or {@link LockMode#PESSIMISTIC_WRITE}, or - *
  • immediately increment the version of the given instance by requesting - * {@link LockMode#PESSIMISTIC_FORCE_INCREMENT}. - *
- *

- * If the requested lock mode is already held on the given entity, this - * operation has no effect. - *

- * This operation cascades to associated instances if the association is - * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. - *

- * The modes {@link LockMode#WRITE} and {@link LockMode#UPGRADE_SKIPLOCKED} - * are not legal arguments to {@code lock()}. - * - * @param object a persistent instance associated with this session - * @param lockMode the lock level - * - * @see #lock(Object, LockModeType) - */ + /// Obtain the specified lock level on the given managed instance associated + /// with this session. This operation may be used to: + /// + /// - perform a version check on an entity read from the second-level cache + /// by requesting [LockMode#READ], + /// - schedule a version check at transaction commit by requesting + /// [LockMode#OPTIMISTIC], + /// - schedule a version increment at transaction commit by requesting + /// [LockMode#OPTIMISTIC_FORCE_INCREMENT] + /// - upgrade to a pessimistic lock with [LockMode#PESSIMISTIC_READ] + /// or [LockMode#PESSIMISTIC_WRITE], or + /// - immediately increment the version of the given instance by requesting + /// [LockMode#PESSIMISTIC_FORCE_INCREMENT]. + /// + /// If the requested lock mode is already held on the given entity, this + /// operation has no effect. + /// + /// This operation cascades to associated instances if the association is + /// mapped with [org.hibernate.annotations.CascadeType#LOCK]. + /// + /// The modes [LockMode#WRITE] and [LockMode#UPGRADE_SKIPLOCKED] + /// are not legal arguments to `lock()`. + /// + /// @param object a persistent instance associated with this session + /// @param lockMode the lock level + /// + /// @see #lock(Object, LockModeType) void lock(Object object, LockMode lockMode); - /** - * Obtain the specified lock level on the given managed instance associated - * with this session, applying any other specified options. This operation may - * be used to: - *

    - *
  • perform a version check on an entity read from the second-level cache - * by requesting {@link LockMode#READ}, - *
  • schedule a version check at transaction commit by requesting - * {@link LockMode#OPTIMISTIC}, - *
  • schedule a version increment at transaction commit by requesting - * {@link LockMode#OPTIMISTIC_FORCE_INCREMENT} - *
  • upgrade to a pessimistic lock with {@link LockMode#PESSIMISTIC_READ} - * or {@link LockMode#PESSIMISTIC_WRITE}, or - *
  • immediately increment the version of the given instance by requesting - * {@link LockMode#PESSIMISTIC_FORCE_INCREMENT}. - *
- *

- * If the requested lock mode is already held on the given entity, this - * operation has no effect. - *

- * This operation cascades to associated instances if the association is - * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. - *

- * The modes {@link LockMode#WRITE} and {@link LockMode#UPGRADE_SKIPLOCKED} - * are not legal arguments to {@code lock()}. - * - * @param object a persistent instance associated with this session - * @param lockMode the lock level - * - * @see #lock(Object, LockModeType, LockOption...) - */ + /// Obtain the specified lock level on the given managed instance associated + /// with this session, applying any other specified options. This operation may + /// be used to: + /// + /// - perform a version check on an entity read from the second-level cache + /// by requesting [LockMode#READ], + /// - schedule a version check at transaction commit by requesting + /// [LockMode#OPTIMISTIC], + /// - schedule a version increment at transaction commit by requesting + /// [LockMode#OPTIMISTIC_FORCE_INCREMENT] + /// - upgrade to a pessimistic lock with [LockMode#PESSIMISTIC_READ] + /// or [LockMode#PESSIMISTIC_WRITE], or + /// - immediately increment the version of the given instance by requesting + /// [LockMode#PESSIMISTIC_FORCE_INCREMENT]. + /// + /// If the requested lock mode is already held on the given entity, this + /// operation has no effect. + /// + /// This operation cascades to associated instances if the association is + /// mapped with [org.hibernate.annotations.CascadeType#LOCK]. + /// + /// The modes [LockMode#WRITE] and [LockMode#UPGRADE_SKIPLOCKED] + /// are not legal arguments to `lock()`. + /// + /// @param object a persistent instance associated with this session + /// @param lockMode the lock level + /// + /// @see #lock(Object, LockModeType, LockOption...) void lock(Object object, LockMode lockMode, LockOption... lockOptions); - /** - * Reread the state of the given managed instance associated with this session - * from the underlying database. This may be useful: - *

    - *
  • when a database trigger alters the object state upon insert or update, - *
  • after {@linkplain #createMutationQuery(String) executing} any HQL update - * or delete statement, - *
  • after {@linkplain #createNativeMutationQuery(String) executing} a native - * SQL statement, or - *
  • after inserting a {@link java.sql.Blob} or {@link java.sql.Clob}. - *
- *

- * This operation cascades to associated instances if the association is mapped - * with {@link jakarta.persistence.CascadeType#REFRESH}. - *

- * This operation requests {@link LockMode#READ}. To obtain a stronger lock, - * call {@link #refresh(Object, RefreshOption...)}, passing the appropriate - * {@link LockMode} as an option. - * - * @param object a persistent instance associated with this session - */ + /// Reread the state of the given managed instance associated with this session + /// from the underlying database. This may be useful: + /// + /// - when a database trigger alters the object state upon insert or update, + /// - after {@linkplain #createMutationQuery(String) executing} any HQL update + /// or delete statement, + /// - after {@linkplain #createNativeMutationQuery(String) executing} a native + /// SQL statement, or + /// - after inserting a [java.sql.Blob] or [java.sql.Clob]. + /// + /// This operation cascades to associated instances if the association is mapped + /// with [jakarta.persistence.CascadeType#REFRESH]. + /// + /// This operation requests [LockMode#READ]. To obtain a stronger lock, + /// call [#refresh(Object,RefreshOption...)], passing the appropriate + /// [LockMode] as an option. + /// + /// @param object a persistent instance associated with this session @Override void refresh(Object object); - /** - * Mark a persistence instance associated with this session for removal from - * the underlying database. Ths operation cascades to associated instances if - * the association is mapped {@link jakarta.persistence.CascadeType#REMOVE}. - *

- * Except when operating in fully JPA-compliant mode, this operation does, - * contrary to the JPA specification, accept a detached entity instance. - * - * @param object the managed persistent instance to remove, or a detached - * instance unless operating in fully JPA-compliant mode - */ + /// Mark a persistence instance associated with this session for removal from + /// the underlying database. Ths operation cascades to associated instances if + /// the association is mapped [jakarta.persistence.CascadeType#REMOVE]. + /// + /// Except when operating in fully JPA-compliant mode, this operation does, + /// contrary to the JPA specification, accept a detached entity instance. + /// + /// @param object the managed persistent instance to remove, or a detached + /// instance unless operating in fully JPA-compliant mode @Override void remove(Object object); - /** - * Determine the current {@linkplain LockMode lock mode} held on the given - * managed instance associated with this session. - *

- * Unlike the JPA-standard {@link #getLockMode}, this operation may be - * called when no transaction is active, in which case it should return - * {@link LockMode#NONE}, indicating that no pessimistic lock is held on - * the given entity. - * - * @param object a persistent instance associated with this session - * - * @return the lock mode currently held on the given entity - * - * @throws IllegalStateException if the given instance is not associated - * with this persistence context - * @throws ObjectDeletedException if the given instance was already - * {@linkplain #remove removed} - */ + /// Determine the current [lock mode][LockMode] held on the given + /// managed instance associated with this session. + /// + /// Unlike the JPA-standard [#getLockMode], this operation may be + /// called when no transaction is active, in which case it should return + /// [LockMode#NONE], indicating that no pessimistic lock is held on + /// the given entity. + /// + /// @param object a persistent instance associated with this session + /// + /// @return the lock mode currently held on the given entity + /// + /// @throws IllegalStateException if the given instance is not associated + /// with this persistence context + /// @throws ObjectDeletedException if the given instance was already + /// {@linkplain #remove removed} LockMode getCurrentLockMode(Object object); - /** - * Completely clear the persistence context. Evict all loaded instances, - * causing every managed entity currently associated with this session to - * transition to the detached state, and cancel all pending insertions, - * updates, and deletions. - *

- * Does not close open iterators or instances of {@link ScrollableResults}. - */ + /// Completely clear the persistence context. Evict all loaded instances, + /// causing every managed entity currently associated with this session to + /// transition to the detached state, and cancel all pending insertions, + /// updates, and deletions. + /// + /// Does not close open iterators or instances of [ScrollableResults]. @Override void clear(); - /** - * Return the persistent instance of the given entity class with the given identifier, - * or null if there is no such persistent instance. If the instance is already associated - * with the session, return that instance. This method never returns an uninitialized - * instance. - *

- * The object returned by {@code get()} or {@code find()} is either an unproxied instance - * of the given entity class, or a fully-fetched proxy object. - *

- * This operation requests {@link LockMode#NONE}, that is, no lock, allowing the object - * to be retrieved from the cache without the cost of database access. However, if it is - * necessary to read the state from the database, the object will be returned with the - * lock mode {@link LockMode#READ}. - *

- * To bypass the second-level cache, and ensure that the state is read from the database, - * either: - *

    - *
  • call {@link #get(Class, Object, LockMode)} with the explicit lock mode - * {@link LockMode#READ}, or - *
  • {@linkplain #setCacheMode set the cache mode} to {@link CacheMode#IGNORE} - * before calling this method. - *
- * - * @apiNote This operation is very similar to {@link #find(Class, Object)}. - * - * @param entityType the entity type - * @param id an identifier - * - * @return a persistent instance or null - * - * @deprecated Because the semantics of this method may change in a future release. - * Use {@link #find(Class, Object)} instead. - */ + /// Return the persistent instance of the given entity class with the given identifier, + /// or null if there is no such persistent instance. If the instance is already associated + /// with the session, return that instance. This method never returns an uninitialized + /// instance. + /// + /// The object returned by `get()` or `find()` is either an unproxied instance + /// of the given entity class, or a fully-fetched proxy object. + /// + /// This operation requests [LockMode#NONE], that is, no lock, allowing the object + /// to be retrieved from the cache without the cost of database access. However, if it is + /// necessary to read the state from the database, the object will be returned with the + /// lock mode [LockMode#READ]. + /// + /// To bypass the second-level cache, and ensure that the state is read from the database, + /// either: + /// + /// - call [#get(Class,Object,LockMode)] with the explicit lock mode + /// [LockMode#READ], or + /// - {@linkplain #setCacheMode set the cache mode} to [CacheMode#IGNORE] + /// before calling this method. + /// + /// @apiNote This operation is very similar to [#find(Class,Object)]. + /// + /// @param entityType the entity type + /// @param id an identifier + /// + /// @return a persistent instance or null + /// + /// @deprecated Because the semantics of this method may change in a future release. + /// Use [#find(Class,Object)] instead. @Deprecated(since = "7.0", forRemoval = true) T get(Class entityType, Object id); - /** - * Return the persistent instance of the given entity class with the given identifier, - * or null if there is no such persistent instance. If the instance is already associated - * with the session, return that instance. This method never returns an uninitialized - * instance. Obtain the specified lock mode if the instance exists. - * - * @apiNote This operation is very similar to {@link #find(Class, Object, LockModeType)}. - * - * @param entityType the entity type - * @param id an identifier - * @param lockMode the lock mode - * - * @return a persistent instance or null - * - * @deprecated Use {@link #find(Class, Object, FindOption...)} instead. - */ + /// Return the persistent instance of the given entity class with the given identifier, + /// or null if there is no such persistent instance. If the instance is already associated + /// with the session, return that instance. This method never returns an uninitialized + /// instance. Obtain the specified lock mode if the instance exists. + /// + /// @apiNote This operation is very similar to [#find(Class,Object,LockModeType)]. + /// + /// @param entityType the entity type + /// @param id an identifier + /// @param lockMode the lock mode + /// + /// @return a persistent instance or null + /// + /// @deprecated Use [#find(Class,Object,FindOption...)] instead. @Deprecated(since = "7.0", forRemoval = true) T get(Class entityType, Object id, LockMode lockMode); - /** - * Return the persistent instance of the given named entity with the given identifier, - * or null if there is no such persistent instance. If the instance is already associated - * with the session, return that instance. This method never returns an uninitialized - * instance. - * - * @param entityName the entity name - * @param id an identifier - * - * @return a persistent instance or null - * - * @deprecated The semantics of this method may change in a future release. - * Use {@link SessionFactory#createGraphForDynamicEntity(String)} - * together with {@link #find(EntityGraph, Object, FindOption...)} - * to load {@link org.hibernate.metamodel.RepresentationMode#MAP - * dynamic entities}. - * - * @see SessionFactory#createGraphForDynamicEntity(String) - * @see #find(EntityGraph, Object, FindOption...) - */ + /// Return the persistent instance of the given named entity with the given identifier, + /// or null if there is no such persistent instance. If the instance is already associated + /// with the session, return that instance. This method never returns an uninitialized + /// instance. + /// + /// @param entityName the entity name + /// @param id an identifier + /// + /// @return a persistent instance or null + /// + /// @deprecated The semantics of this method may change in a future release. + /// Use [SessionFactory#createGraphForDynamicEntity(String)] + /// together with [#find(EntityGraph,Object,FindOption...)] + /// to load [dynamic entities][org.hibernate.metamodel.RepresentationMode#MAP]. + /// + /// @see SessionFactory#createGraphForDynamicEntity(String) + /// @see #find(EntityGraph, Object, FindOption...) @Deprecated(since = "7", forRemoval = true) Object get(String entityName, Object id); - /** - * Return the persistent instance of the given entity class with the given identifier, - * or null if there is no such persistent instance. If the instance is already associated - * with the session, return that instance. This method never returns an uninitialized - * instance. Obtain the specified lock mode if the instance exists. - * - * @param entityName the entity name - * @param id an identifier - * @param lockMode the lock mode - * - * @return a persistent instance or null - * - * @see #get(String, Object, LockOptions) - * - * @deprecated The semantics of this method may change in a future release. - */ + /// Return the persistent instance of the given entity class with the given identifier, + /// or null if there is no such persistent instance. If the instance is already associated + /// with the session, return that instance. This method never returns an uninitialized + /// instance. Obtain the specified lock mode if the instance exists. + /// + /// @param entityName the entity name + /// @param id an identifier + /// @param lockMode the lock mode + /// + /// @return a persistent instance or null + /// + /// @see #get(String, Object, LockOptions) + /// + /// @deprecated The semantics of this method may change in a future release. @Deprecated(since = "7.0", forRemoval = true) Object get(String entityName, Object id, LockMode lockMode); - /** - * Return the persistent instance of the given entity class with the given identifier, - * or null if there is no such persistent instance. If the instance is already associated - * with the session, return that instance. This method never returns an uninitialized - * instance. Obtain the specified lock mode if the instance exists. - * - * @param entityType the entity type - * @param id an identifier - * @param lockOptions the lock mode - * - * @return a persistent instance or null - * - * @deprecated This method will be removed. - * Use {@link #find(Class, Object, FindOption...)} instead. - */ + /// Return the persistent instance of the given entity class with the given identifier, + /// or null if there is no such persistent instance. If the instance is already associated + /// with the session, return that instance. This method never returns an uninitialized + /// instance. Obtain the specified lock mode if the instance exists. + /// + /// @param entityType the entity type + /// @param id an identifier + /// @param lockOptions the lock mode + /// + /// @return a persistent instance or null + /// + /// @deprecated This method will be removed. + /// Use [#find(Class,Object,FindOption...)] instead. @Deprecated(since = "7.0", forRemoval = true) T get(Class entityType, Object id, LockOptions lockOptions); - /** - * Return the persistent instance of the given entity class with the given identifier, - * or null if there is no such persistent instance. If the instance is already associated - * with the session, return that instance. This method never returns an uninitialized - * instance. Obtain the specified lock mode if the instance exists. - * - * @param entityName the entity name - * @param id an identifier - * @param lockOptions contains the lock mode - * - * @return a persistent instance or null - * - * @deprecated This method will be removed. - * Use {@link SessionFactory#createGraphForDynamicEntity(String)} - * together with {@link #find(EntityGraph, Object, FindOption...)} - * to load {@link org.hibernate.metamodel.RepresentationMode#MAP - * dynamic entities}. - */ + /// Return the persistent instance of the given entity class with the given identifier, + /// or null if there is no such persistent instance. If the instance is already associated + /// with the session, return that instance. This method never returns an uninitialized + /// instance. Obtain the specified lock mode if the instance exists. + /// + /// @param entityName the entity name + /// @param id an identifier + /// @param lockOptions contains the lock mode + /// + /// @return a persistent instance or null + /// + /// @deprecated This method will be removed. + /// Use [SessionFactory#createGraphForDynamicEntity(String)] + /// together with [#find(EntityGraph,Object,FindOption...)] + /// to load [dynamic entities][org.hibernate.metamodel.RepresentationMode#MAP]. @Deprecated(since = "7.0", forRemoval = true) Object get(String entityName, Object id, LockOptions lockOptions); - /** - * Obtain a lock on the given managed instance associated with this session, - * using the given {@linkplain LockOptions lock options}. - *

- * This operation cascades to associated instances if the association is - * mapped with {@link org.hibernate.annotations.CascadeType#LOCK}. - * - * @param object a persistent instance associated with this session - * @param lockOptions the lock options - * - * @since 6.2 - * - * @deprecated This method will be removed. - * Use {@linkplain #lock(Object, LockModeType, LockOption...)} instead - */ + /// Obtain a lock on the given managed instance associated with this session, + /// using the given [lock options][LockOptions]. + /// + /// This operation cascades to associated instances if the association is + /// mapped with [org.hibernate.annotations.CascadeType#LOCK]. + /// + /// @param object a persistent instance associated with this session + /// @param lockOptions the lock options + /// + /// @since 6.2 + /// + /// @deprecated This method will be removed. + /// Use [#lock(Object, LockModeType, LockOption...)] instead @Deprecated(since = "7.0", forRemoval = true) void lock(Object object, LockOptions lockOptions); - /** - * Reread the state of the given managed instance from the underlying database, - * obtaining the given {@link LockMode}. - * - * @param object a persistent instance associated with this session - * @param lockOptions contains the lock mode to use - * - * @deprecated This method will be removed. - * Use {@linkplain #refresh(Object, RefreshOption...)} instead - */ + /// Reread the state of the given managed instance from the underlying database, + /// obtaining the given [LockMode]. + /// + /// @param object a persistent instance associated with this session + /// @param lockOptions contains the lock mode to use + /// + /// @deprecated This method will be removed. + /// Use [#refresh(Object, RefreshOption...)] instead @Deprecated(since = "7.0", forRemoval = true) void refresh(Object object, LockOptions lockOptions); - /** - * Return the entity name for the given persistent entity. - *

- * If the given entity is an uninitialized proxy, the proxy is initialized by - * side effect. - * - * @param object a persistent entity associated with this session - * - * @return the entity name - */ + /// Return the entity name for the given persistent entity. + /// + /// If the given entity is an uninitialized proxy, the proxy is initialized by + /// side effect. + /// + /// @param object a persistent entity associated with this session + /// + /// @return the entity name String getEntityName(Object object); - /** - * Return a reference to the persistent instance with the given class and identifier, - * making the assumption that the instance is still persistent in the database. This - * method never results in access to the underlying data store, and thus might return - * a proxy that is initialized on-demand, when a non-identifier method is accessed. - *

- * Note that {@link Hibernate#createDetachedProxy(SessionFactory, Class, Object)} - * may be used to obtain a detached reference. - *

- * It's sometimes necessary to narrow a reference returned by {@code getReference()} - * to a subtype of the given entity type. A direct Java typecast should never be used - * in this situation. Instead, the method {@link Hibernate#unproxy(Object, Class)} is - * the recommended way to narrow the type of a proxy object. Alternatively, a new - * reference may be obtained by simply calling {@code getReference()} again, passing - * the subtype. Either way, the narrowed reference will usually not be identical to - * the original reference, when the references are compared using the {@code ==} - * operator. - * - * @param entityType the entity type - * @param id the identifier of a persistent instance that exists in the database - * - * @return the persistent instance or proxy - */ + /// Return a reference to the persistent instance with the given class and identifier, + /// making the assumption that the instance is still persistent in the database. This + /// method never results in access to the underlying data store, and thus might return + /// a proxy that is initialized on-demand, when a non-identifier method is accessed. + /// + /// Note that [Hibernate#createDetachedProxy(SessionFactory,Class,Object)] + /// may be used to obtain a _detached_ reference. + /// + /// It's sometimes necessary to narrow a reference returned by `getReference()` + /// to a subtype of the given entity type. A direct Java typecast should never be used + /// in this situation. Instead, the method [Hibernate#unproxy(Object,Class)] is + /// the recommended way to narrow the type of a proxy object. Alternatively, a new + /// reference may be obtained by simply calling `getReference()` again, passing + /// the subtype. Either way, the narrowed reference will usually not be identical to + /// the original reference, when the references are compared using the `==` + /// operator. + /// + /// @param entityType the entity type + /// @param id the identifier of a persistent instance that exists in the database + /// + /// @return the persistent instance or proxy @Override T getReference(Class entityType, Object id); - /** - * Return a reference to the persistent instance of the given named entity with the - * given identifier, making the assumption that the instance is still persistent in - * the database. This method never results in access to the underlying data store, - * and thus might return a proxy that is initialized on-demand, when a non-identifier - * method is accessed. - * - * @param entityName the entity name - * @param id the identifier of a persistent instance that exists in the database - * - * @return the persistent instance or proxy - */ + /// Return a reference to the persistent instance of the given named entity with the + /// given identifier, making the assumption that the instance is still persistent in + /// the database. This method never results in access to the underlying data store, + /// and thus might return a proxy that is initialized on-demand, when a non-identifier + /// method is accessed. + /// + /// @param entityName the entity name + /// @param id the identifier of a persistent instance that exists in the database + /// + /// @return the persistent instance or proxy Object getReference(String entityName, Object id); - /** - * Return a reference to the persistent instance with the same identity as the given - * instance, which might be detached, making the assumption that the instance is still - * persistent in the database. This method never results in access to the underlying - * data store, and thus might return a proxy that is initialized on-demand, when a - * non-identifier method is accessed. - * - * @param object a detached persistent instance - * - * @return the persistent instance or proxy - * - * @since 6.0 - */ + /// Return a reference to the persistent instance with the same identity as the given + /// instance, which might be detached, making the assumption that the instance is still + /// persistent in the database. This method never results in access to the underlying + /// data store, and thus might return a proxy that is initialized on-demand, when a + /// non-identifier method is accessed. + /// + /// @param object a detached persistent instance + /// + /// @return the persistent instance or proxy + /// + /// @since 6.0 @Override T getReference(T object); - /** - * Create an {@link IdentifierLoadAccess} instance to retrieve an instance of the given - * entity type by its primary key. - * - * @param entityClass the entity type to be retrieved - * - * @return an instance of {@link IdentifierLoadAccess} for executing the lookup - * - * @throws HibernateException If the given class does not resolve as a mapped entity - * - * @deprecated This method will be removed. - * Use {@link #find(Class, Object, FindOption...)} instead. - * See {@link FindOption}. - */ + /// Create an [IdentifierLoadAccess] instance to retrieve an instance of the given + /// entity type by its primary key. + /// + /// @param entityClass the entity type to be retrieved + /// + /// @return an instance of [IdentifierLoadAccess] for executing the lookup + /// + /// @throws HibernateException If the given class does not resolve as a mapped entity + /// + /// @deprecated This method will be removed. + /// Use [#find(Class,Object,FindOption...)] instead. + /// See [FindOption]. @Deprecated(since = "7.1", forRemoval = true) IdentifierLoadAccess byId(Class entityClass); - /** - * Create an {@link IdentifierLoadAccess} instance to retrieve an instance of the named - * entity type by its primary key. - * - * @param entityName the entity name of the entity type to be retrieved - * - * @return an instance of {@link IdentifierLoadAccess} for executing the lookup - * - * @throws HibernateException If the given name does not resolve to a mapped entity - * - * @deprecated This method will be removed. - * Use {@link #find(String, Object, FindOption...)} instead. - * See {@link FindOption}. - */ + /// Create an [IdentifierLoadAccess] instance to retrieve an instance of the named + /// entity type by its primary key. + /// + /// @param entityName the entity name of the entity type to be retrieved + /// + /// @return an instance of [IdentifierLoadAccess] for executing the lookup + /// + /// @throws HibernateException If the given name does not resolve to a mapped entity + /// + /// @deprecated This method will be removed. + /// Use [#find(String,Object,FindOption...)] instead. + /// See [FindOption]. @Deprecated(since = "7.1", forRemoval = true) IdentifierLoadAccess byId(String entityName); - /** - * Create a {@link MultiIdentifierLoadAccess} instance to retrieve multiple instances - * of the given entity type by their primary key values, using batching. - * - * @param entityClass the entity type to be retrieved - * - * @return an instance of {@link MultiIdentifierLoadAccess} for executing the lookup - * - * @throws HibernateException If the given class does not resolve as a mapped entity - * - * @see #findMultiple(Class, List, FindOption...) - * - * @deprecated Use {@link #findMultiple(Class, List, FindOption...)} instead. - */ + /// Create a [MultiIdentifierLoadAccess] instance to retrieve multiple instances + /// of the given entity type by their primary key values, using batching. + /// + /// @param entityClass the entity type to be retrieved + /// + /// @return an instance of [MultiIdentifierLoadAccess] for executing the lookup + /// + /// @throws HibernateException If the given class does not resolve as a mapped entity + /// + /// @see #findMultiple(Class, List, FindOption...) + /// + /// @deprecated Use [#findMultiple(Class,List,FindOption...)] instead. @Deprecated(since = "7.2", forRemoval = true) MultiIdentifierLoadAccess byMultipleIds(Class entityClass); - /** - * Create a {@link MultiIdentifierLoadAccess} instance to retrieve multiple instances - * of the named entity type by their primary key values, using batching. - * - * @param entityName the entity name of the entity type to be retrieved - * - * @return an instance of {@link MultiIdentifierLoadAccess} for executing the lookup - * - * @throws HibernateException If the given name does not resolve to a mapped entity - * - * @deprecated Use {@link #findMultiple(EntityGraph, List, FindOption...)} instead, - * with {@linkplain SessionFactory#createGraphForDynamicEntity(String)}. - */ + /// Create a [MultiIdentifierLoadAccess] instance to retrieve multiple instances + /// of the named entity type by their primary key values, using batching. + /// + /// @param entityName the entity name of the entity type to be retrieved + /// + /// @return an instance of [MultiIdentifierLoadAccess] for executing the lookup + /// + /// @throws HibernateException If the given name does not resolve to a mapped entity + /// + /// @deprecated Use [#findMultiple(EntityGraph,List,FindOption...)] instead, + /// with {@linkplain SessionFactory#createGraphForDynamicEntity(String)}. @Deprecated(since = "7.2", forRemoval = true) MultiIdentifierLoadAccess byMultipleIds(String entityName); - /** - * Create a {@link NaturalIdLoadAccess} instance to retrieve an instance of the given - * entity type by its {@linkplain org.hibernate.annotations.NaturalId natural id}, - * which may be a composite natural id. The entity must have at least one attribute - * annotated {@link org.hibernate.annotations.NaturalId}. - * - * @param entityClass the entity type to be retrieved - * - * @return an instance of {@link NaturalIdLoadAccess} for executing the lookup - * - * @throws HibernateException If the given class does not resolve as a mapped entity, - * or if the entity does not declare a natural id - */ + /// Create a [NaturalIdLoadAccess] instance to retrieve an instance of the given + /// entity type by its {@linkplain org.hibernate.annotations.NaturalId natural id}, + /// which may be a composite natural id. The entity must have at least one attribute + /// annotated [org.hibernate.annotations.NaturalId]. + /// + /// @param entityClass the entity type to be retrieved + /// + /// @return an instance of [NaturalIdLoadAccess] for executing the lookup + /// + /// @throws HibernateException If the given class does not resolve as a mapped entity, + /// or if the entity does not declare a natural id + /// + /// @deprecated (since 7.3) : Use {@linkplain #find} with [KeyType#NATURAL] instead. + @Deprecated NaturalIdLoadAccess byNaturalId(Class entityClass); - /** - * Create a {@link NaturalIdLoadAccess} instance to retrieve an instance of the named - * entity type by its {@linkplain org.hibernate.annotations.NaturalId natural id}, - * which may be a composite natural id. The entity must have at least one attribute - * annotated {@link org.hibernate.annotations.NaturalId}. - * - * @param entityName the entity name of the entity type to be retrieved - * - * @return an instance of {@link NaturalIdLoadAccess} for executing the lookup - * - * @throws HibernateException If the given name does not resolve to a mapped entity, - * or if the entity does not declare a natural id - */ + /// Create a [NaturalIdLoadAccess] instance to retrieve an instance of the named + /// entity type by its {@linkplain org.hibernate.annotations.NaturalId natural id}, + /// which may be a composite natural id. The entity must have at least one attribute + /// annotated [org.hibernate.annotations.NaturalId]. + /// + /// @param entityName the entity name of the entity type to be retrieved + /// + /// @return an instance of [NaturalIdLoadAccess] for executing the lookup + /// + /// @throws HibernateException If the given name does not resolve to a mapped entity, + /// or if the entity does not declare a natural id + /// + /// @deprecated (since 7.3) : Use {@linkplain #find} with [KeyType#NATURAL] instead. + @Deprecated NaturalIdLoadAccess byNaturalId(String entityName); - /** - * Create a {@link SimpleNaturalIdLoadAccess} instance to retrieve an instance of the - * given entity type by its {@linkplain org.hibernate.annotations.NaturalId natural id}, - * which must be a simple (non-composite) value. The entity must have exactly one - * attribute annotated {@link org.hibernate.annotations.NaturalId}. - * - * @param entityClass the entity type to be retrieved - * - * @return an instance of {@link SimpleNaturalIdLoadAccess} for executing the lookup - * - * @throws HibernateException If the given class does not resolve as a mapped entity, - * or if the entity does not declare a natural id - */ + /// Create a [SimpleNaturalIdLoadAccess] instance to retrieve an instance of the + /// given entity type by its {@linkplain org.hibernate.annotations.NaturalId natural id}, + /// which must be a simple (non-composite) value. The entity must have exactly one + /// attribute annotated [org.hibernate.annotations.NaturalId]. + /// + /// @param entityClass the entity type to be retrieved + /// + /// @return an instance of [SimpleNaturalIdLoadAccess] for executing the lookup + /// + /// @throws HibernateException If the given class does not resolve as a mapped entity, + /// or if the entity does not declare a natural id + /// + /// @deprecated (since 7.3) : Use {@linkplain #find} with [KeyType#NATURAL] instead. + @Deprecated SimpleNaturalIdLoadAccess bySimpleNaturalId(Class entityClass); - /** - * Create a {@link SimpleNaturalIdLoadAccess} instance to retrieve an instance of the - * named entity type by its {@linkplain org.hibernate.annotations.NaturalId natural id}, - * which must be a simple (non-composite) value. The entity must have exactly one - * attribute annotated {@link org.hibernate.annotations.NaturalId}. - * - * @param entityName the entity name of the entity type to be retrieved - * - * @return an instance of {@link SimpleNaturalIdLoadAccess} for executing the lookup - * - * @throws HibernateException If the given name does not resolve to a mapped entity, - * or if the entity does not declare a natural id - */ + /// Create a [SimpleNaturalIdLoadAccess] instance to retrieve an instance of the + /// named entity type by its {@linkplain org.hibernate.annotations.NaturalId natural id}, + /// which must be a simple (non-composite) value. The entity must have exactly one + /// attribute annotated [org.hibernate.annotations.NaturalId]. + /// + /// @param entityName the entity name of the entity type to be retrieved + /// + /// @return an instance of [SimpleNaturalIdLoadAccess] for executing the lookup + /// + /// @throws HibernateException If the given name does not resolve to a mapped entity, + /// or if the entity does not declare a natural id + /// + /// @deprecated (since 7.3) : Use {@linkplain #find} with [KeyType#NATURAL] instead. + @Deprecated SimpleNaturalIdLoadAccess bySimpleNaturalId(String entityName); - /** - * Create a {@link MultiIdentifierLoadAccess} instance to retrieve multiple instances - * of the given entity type by their by {@linkplain org.hibernate.annotations.NaturalId - * natural id} values, using batching. - * - * @param entityClass the entity type to be retrieved - * - * @return an instance of {@link NaturalIdMultiLoadAccess} for executing the lookup - * - * @throws HibernateException If the given class does not resolve as a mapped entity, - * or if the entity does not declare a natural id - */ + /// Create a [MultiIdentifierLoadAccess] instance to retrieve multiple instances + /// of the given entity type by their by {@linkplain org.hibernate.annotations.NaturalId + /// natural id} values, using batching. + /// + /// @param entityClass the entity type to be retrieved + /// + /// @return an instance of [NaturalIdMultiLoadAccess] for executing the lookup + /// + /// @throws HibernateException If the given class does not resolve as a mapped entity, + /// or if the entity does not declare a natural id + /// + /// @deprecated (since 7.3) : Use {@linkplain #findMultiple} with [KeyType#NATURAL] instead. + /// + @Deprecated NaturalIdMultiLoadAccess byMultipleNaturalId(Class entityClass); - /** - * Create a {@link MultiIdentifierLoadAccess} instance to retrieve multiple instances - * of the named entity type by their by {@linkplain org.hibernate.annotations.NaturalId - * natural id} values, using batching. - * - * @param entityName the entity name of the entity type to be retrieved - * - * @return an instance of {@link NaturalIdMultiLoadAccess} for executing the lookup - * - * @throws HibernateException If the given name does not resolve to a mapped entity, - * or if the entity does not declare a natural id - */ + /// Create a [MultiIdentifierLoadAccess] instance to retrieve multiple instances + /// of the named entity type by their by {@linkplain org.hibernate.annotations.NaturalId + /// natural id} values, using batching. + /// + /// @param entityName the entity name of the entity type to be retrieved + /// + /// @return an instance of [NaturalIdMultiLoadAccess] for executing the lookup + /// + /// @throws HibernateException If the given name does not resolve to a mapped entity, + /// or if the entity does not declare a natural id + /// + /// @deprecated (since 7.3) : Use {@linkplain #findMultiple} with [KeyType#NATURAL] instead. + @Deprecated NaturalIdMultiLoadAccess byMultipleNaturalId(String entityName); - /** - * Get the {@linkplain SessionStatistics statistics} for this session. - * - * @return the session statistics being collected for this session - */ + /// Get the [statistics][SessionStatistics] for this session. + /// + /// @return the session statistics being collected for this session SessionStatistics getStatistics(); - /** - * Is the specified entity or proxy read-only? - *

- * To get the default read-only/modifiable setting used for - * entities and proxies that are loaded into the session use - * {@link #isDefaultReadOnly()} - * - * @see #isDefaultReadOnly() - * - * @param entityOrProxy an entity or proxy - * @return {@code true} if the entity or proxy is read-only, - * {@code false} if the entity or proxy is modifiable. - */ + /// Is the specified entity or proxy read-only? + /// + /// To get the default read-only/modifiable setting used for + /// entities and proxies that are loaded into the session use + /// [#isDefaultReadOnly()] + /// + /// @see #isDefaultReadOnly() + /// + /// @param entityOrProxy an entity or proxy + /// @return `true` if the entity or proxy is read-only, + /// `false` if the entity or proxy is modifiable. boolean isReadOnly(Object entityOrProxy); - /** - * Set an unmodified persistent object to read-only mode, or a read-only - * object to modifiable mode. In read-only mode, no snapshot is maintained, - * the instance is never dirty-checked, and mutations to the fields of the - * entity are not made persistent. - *

- * If the entity or proxy already has the specified read-only/modifiable - * setting, then this method does nothing. - *

- * To set the default read-only/modifiable setting used for all entities - * and proxies that are loaded into the session use - * {@link #setDefaultReadOnly(boolean)}. - *

- * To override the default read-only mode of the current session for - * all entities and proxies returned by a given {@code Query}, use - * {@link Query#setReadOnly(boolean)}. - *

- * Every instance of an {@linkplain org.hibernate.annotations.Immutable - * immutable} entity is loaded in read-only mode. An immutable entity may - * not be set to modifiable. - * - * @see #setDefaultReadOnly(boolean) - * @see Query#setReadOnly(boolean) - * @see IdentifierLoadAccess#withReadOnly(boolean) - * @see org.hibernate.annotations.Immutable - * - * @param entityOrProxy an entity or proxy - * @param readOnly {@code true} if the entity or proxy should be made read-only; - * {@code false} if the entity or proxy should be made modifiable - * - * @throws IllegalStateException if an immutable entity is set to modifiable - */ + /// Set an unmodified persistent object to read-only mode, or a read-only + /// object to modifiable mode. In read-only mode, no snapshot is maintained, + /// the instance is never dirty-checked, and mutations to the fields of the + /// entity are not made persistent. + /// + /// If the entity or proxy already has the specified read-only/modifiable + /// setting, then this method does nothing. + /// + /// To set the default read-only/modifiable setting used for all entities + /// and proxies that are loaded into the session use + /// [#setDefaultReadOnly(boolean)]. + /// + /// To override the default read-only mode of the current session for + /// all entities and proxies returned by a given `Query`, use + /// [Query#setReadOnly(boolean)]. + /// + /// Every instance of an [immutable][org.hibernate.annotations.Immutable] + /// entity is loaded in read-only mode. An immutable entity may + /// not be set to modifiable. + /// + /// @see #setDefaultReadOnly(boolean) + /// @see Query#setReadOnly(boolean) + /// @see IdentifierLoadAccess#withReadOnly(boolean) + /// @see org.hibernate.annotations.Immutable + /// + /// @param entityOrProxy an entity or proxy + /// @param readOnly `true` if the entity or proxy should be made read-only; + /// `false` if the entity or proxy should be made modifiable + /// + /// @throws IllegalStateException if an immutable entity is set to modifiable void setReadOnly(Object entityOrProxy, boolean readOnly); - /** - * Is the {@link org.hibernate.annotations.FetchProfile fetch profile} - * with the given name enabled in this session? - * - * @param name the name of the profile - * @return True if fetch profile is enabled; false if not. - * - * @throws UnknownProfileException Indicates that the given name does not - * match any known fetch profile names - * - * @see org.hibernate.annotations.FetchProfile - */ + /// Is the [fetch profile][org.hibernate.annotations.FetchProfile] + /// with the given name enabled in this session? + /// + /// @param name the name of the profile + /// @return True if fetch profile is enabled; false if not. + /// + /// @throws UnknownProfileException Indicates that the given name does not + /// match any known fetch profile names + /// + /// @see org.hibernate.annotations.FetchProfile boolean isFetchProfileEnabled(String name) throws UnknownProfileException; - /** - * Enable the {@link org.hibernate.annotations.FetchProfile fetch profile} - * with the given name in this session. If the requested fetch profile is - * already enabled, the call has no effect. - * - * @param name the name of the fetch profile to be enabled - * - * @throws UnknownProfileException Indicates that the given name does not - * match any known fetch profile names - * - * @see org.hibernate.annotations.FetchProfile - */ + /// Enable the [fetch profile][org.hibernate.annotations.FetchProfile] + /// with the given name in this session. If the requested fetch profile is + /// already enabled, the call has no effect. + /// + /// @param name the name of the fetch profile to be enabled + /// + /// @throws UnknownProfileException Indicates that the given name does not + /// match any known fetch profile names + /// + /// @see org.hibernate.annotations.FetchProfile void enableFetchProfile(String name) throws UnknownProfileException; - /** - * Disable the {@link org.hibernate.annotations.FetchProfile fetch profile} - * with the given name in this session. If the requested fetch profile is - * not currently enabled, the call has no effect. - * - * @param name the name of the fetch profile to be disabled - * - * @throws UnknownProfileException Indicates that the given name does not - * match any known fetch profile names - * - * @see org.hibernate.annotations.FetchProfile - */ + /// Disable the [fetch profile][org.hibernate.annotations.FetchProfile] + /// with the given name in this session. If the requested fetch profile is + /// not currently enabled, the call has no effect. + /// + /// @param name the name of the fetch profile to be disabled + /// + /// @throws UnknownProfileException Indicates that the given name does not + /// match any known fetch profile names + /// + /// @see org.hibernate.annotations.FetchProfile void disableFetchProfile(String name) throws UnknownProfileException; - /** - * Obtain a {@linkplain LobHelper} for instances of {@link java.sql.Blob} - * and {@link java.sql.Clob}. - * - * @return an instance of {@link LobHelper} - * - * @deprecated Use {@link Hibernate#getLobHelper()} instead. - */ + /// Obtain a {@linkplain LobHelper} for instances of [java.sql.Blob] + /// and [java.sql.Clob]. + /// + /// @return an instance of [LobHelper] + /// + /// @deprecated Use [#getLobHelper()] instead. @Deprecated(since="7.0", forRemoval = true) LobHelper getLobHelper(); - /** - * Obtain the collection of all managed entities which belong to this - * persistence context. - * - * @since 7.0 - */ + /// Obtain the collection of all managed entities which belong to this + /// persistence context. + /// + /// @since 7.0 @Incubating Collection getManagedEntities(); - /** - * Obtain a collection of all managed instances of the entity type with the - * given entity name which belong to this persistence context. - * - * @since 7.0 - */ + /// Obtain a collection of all managed instances of the entity type with the + /// given entity name which belong to this persistence context. + /// + /// @since 7.0 @Incubating Collection getManagedEntities(String entityName); - /** - * Obtain a collection of all managed entities of the given type which belong - * to this persistence context. This operation is not polymorphic, and does - * not return instances of subtypes of the given entity type. - * - * @since 7.0 - */ + /// Obtain a collection of all managed entities of the given type which belong + /// to this persistence context. This operation is not polymorphic, and does + /// not return instances of subtypes of the given entity type. + /// + /// @since 7.0 @Incubating Collection getManagedEntities(Class entityType); - /** - * Obtain a collection of all managed entities of the given type which belong - * to this persistence context. This operation is not polymorphic, and does - * not return instances of subtypes of the given entity type. - * - * @since 7.0 - */ + /// Obtain a collection of all managed entities of the given type which belong + /// to this persistence context. This operation is not polymorphic, and does + /// not return instances of subtypes of the given entity type. + /// + /// @since 7.0 @Incubating Collection getManagedEntities(EntityType entityType); - /** - * Add one or more listeners to the Session - * - * @param listeners the listener(s) to add - */ + /// Add one or more listeners to the Session + /// + /// @param listeners the listener(s) to add void addEventListeners(SessionEventListener... listeners); - /** - * Set a hint. The hints understood by Hibernate are enumerated by - * {@link org.hibernate.jpa.AvailableHints}. - * - * @see org.hibernate.jpa.HibernateHints - * @see org.hibernate.jpa.SpecHints - * - * @apiNote Hints are a - * {@linkplain jakarta.persistence.EntityManager#setProperty - * JPA-standard way} to control provider-specific behavior of the - * {@code EntityManager}. Clients of the native API defined by - * Hibernate should make use of type-safe operations of this - * interface. For example, {@link #enableFetchProfile(String)} - * should be used in preference to the hint - * {@link org.hibernate.jpa.HibernateHints#HINT_FETCH_PROFILE}. - */ + /// Set a hint. The hints understood by Hibernate are enumerated by + /// [org.hibernate.jpa.AvailableHints]. + /// + /// @see org.hibernate.jpa.HibernateHints + /// @see org.hibernate.jpa.SpecHints + /// + /// @apiNote Hints are a [JPA-standard way][jakarta.persistence.EntityManager#setProperty] + /// to control provider-specific behavior of the + /// [EntityManager]. Clients of the native API defined by + /// Hibernate should make use of type-safe operations of this + /// interface. For example, [#enableFetchProfile(String)] + /// should be used in preference to the hint [org.hibernate.jpa.HibernateHints#HINT_FETCH_PROFILE]. @Override void setProperty(String propertyName, Object value); - /** - * Create a new mutable instance of {@link EntityGraph}, with only - * a root node, allowing programmatic definition of the graph from - * scratch. - * - * @param rootType The root entity of the graph - * - * @see #find(EntityGraph, Object, FindOption...) - * @see org.hibernate.query.SelectionQuery#setEntityGraph(EntityGraph, org.hibernate.graph.GraphSemantic) - * @see org.hibernate.graph.EntityGraphs#createGraph(jakarta.persistence.metamodel.EntityType) - */ + /// Create a new mutable instance of [EntityGraph], with only + /// a root node, allowing programmatic definition of the graph from + /// scratch. + /// + /// @param rootType The root entity of the graph + /// + /// @see #find(EntityGraph, Object, FindOption...) + /// @see org.hibernate.query.SelectionQuery#setEntityGraph(EntityGraph, org.hibernate.graph.GraphSemantic) + /// @see org.hibernate.graph.EntityGraphs#createGraph(jakarta.persistence.metamodel.EntityType) @Override RootGraph createEntityGraph(Class rootType); - /** - * Create a new mutable instance of {@link EntityGraph}, based on - * a predefined {@linkplain jakarta.persistence.NamedEntityGraph - * named entity graph}, allowing customization of the graph, or - * return {@code null} if there is no predefined graph with the - * given name. - * - * @param graphName The name of the predefined named entity graph - * - * @apiNote This method returns {@code RootGraph}, requiring an - * unchecked typecast before use. It's cleaner to obtain a graph using - * {@link #createEntityGraph(Class, String)} instead. - * - * @see #find(EntityGraph, Object, FindOption...) - * @see org.hibernate.query.SelectionQuery#setEntityGraph(EntityGraph, org.hibernate.graph.GraphSemantic) - * @see jakarta.persistence.EntityManagerFactory#addNamedEntityGraph(String, EntityGraph) - */ + /// Create a new mutable instance of [EntityGraph], based on + /// a predefined [named entity graph][jakarta.persistence.NamedEntityGraph], + /// allowing customization of the graph, or return `null` if there is no + /// predefined graph with the given name. + /// + /// @param graphName The name of the predefined named entity graph + /// + /// @apiNote This method returns `RootGraph`, requiring an + /// unchecked typecast before use. It's cleaner to obtain a graph using + /// [#createEntityGraph(Class,String)] instead. + /// + /// @see #find(EntityGraph, Object, FindOption...) + /// @see org.hibernate.query.SelectionQuery#setEntityGraph(EntityGraph, org.hibernate.graph.GraphSemantic) + /// @see jakarta.persistence.EntityManagerFactory#addNamedEntityGraph(String, EntityGraph) @Override RootGraph createEntityGraph(String graphName); - /** - * Obtain an immutable reference to a predefined - * {@linkplain jakarta.persistence.NamedEntityGraph named entity graph} - * or return {@code null} if there is no predefined graph with the given - * name. - * - * @param graphName The name of the predefined named entity graph - * - * @apiNote This method returns {@code RootGraph}, requiring an - * unchecked typecast before use. It's cleaner to obtain a graph using - * the static metamodel for the class which defines the graph, or by - * calling {@link SessionFactory#getNamedEntityGraphs(Class)} instead. - * - * @see #find(EntityGraph, Object, FindOption...) - * @see org.hibernate.query.SelectionQuery#setEntityGraph(EntityGraph, org.hibernate.graph.GraphSemantic) - * @see jakarta.persistence.EntityManagerFactory#addNamedEntityGraph(String, EntityGraph) - */ + /// Obtain an immutable reference to a predefined + /// [named entity graph][jakarta.persistence.NamedEntityGraph] + /// or return `null` if there is no predefined graph with the given + /// name. + /// + /// @param graphName The name of the predefined named entity graph + /// + /// @apiNote This method returns `RootGraph`, requiring an + /// unchecked typecast before use. It's cleaner to obtain a graph using + /// the static metamodel for the class which defines the graph, or by + /// calling [SessionFactory#getNamedEntityGraphs(Class)] instead. + /// + /// @see #find(EntityGraph, Object, FindOption...) + /// @see org.hibernate.query.SelectionQuery#setEntityGraph(EntityGraph, org.hibernate.graph.GraphSemantic) + /// @see jakarta.persistence.EntityManagerFactory#addNamedEntityGraph(String, EntityGraph) @Override RootGraph getEntityGraph(String graphName); - /** - * Retrieve all named {@link EntityGraph}s with the given root entity type. - * - * @see jakarta.persistence.EntityManagerFactory#getNamedEntityGraphs(Class) - * @see jakarta.persistence.EntityManagerFactory#addNamedEntityGraph(String, EntityGraph) - */ + /// Retrieve all named [EntityGraph]s with the given root entity type. + /// + /// @see jakarta.persistence.EntityManagerFactory#getNamedEntityGraphs(Class) + /// @see jakarta.persistence.EntityManagerFactory#addNamedEntityGraph(String, EntityGraph) @Override List> getEntityGraphs(Class entityClass); @@ -1540,19 +1376,15 @@ public interface Session extends SharedSessionContract, EntityManager { @Override Query createQuery(CriteriaQuery criteriaQuery); - /** - * Create a {@link Query} for the given JPA {@link CriteriaDelete}. - * - * @deprecated use {@link #createMutationQuery(CriteriaDelete)} - */ + /// Create a [Query] for the given JPA [CriteriaDelete]. + /// + /// @deprecated use [#createMutationQuery(CriteriaDelete)] @Override @Deprecated(since = "6.0") @SuppressWarnings("rawtypes") Query createQuery(CriteriaDelete deleteQuery); - /** - * Create a {@link Query} for the given JPA {@link CriteriaUpdate}. - * - * @deprecated use {@link #createMutationQuery(CriteriaUpdate)} - */ + /// Create a [Query] for the given JPA [CriteriaUpdate]. + /// + /// @deprecated use [#createMutationQuery(CriteriaUpdate)] @Override @Deprecated(since = "6.0") @SuppressWarnings("rawtypes") Query createQuery(CriteriaUpdate updateQuery); diff --git a/hibernate-core/src/main/java/org/hibernate/SimpleNaturalIdLoadAccess.java b/hibernate-core/src/main/java/org/hibernate/SimpleNaturalIdLoadAccess.java index 13130fd7c18e..bdcb8a61de8c 100644 --- a/hibernate-core/src/main/java/org/hibernate/SimpleNaturalIdLoadAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/SimpleNaturalIdLoadAccess.java @@ -29,7 +29,10 @@ * @see Session#bySimpleNaturalId(Class) * @see org.hibernate.annotations.NaturalId * @see NaturalIdLoadAccess + * + * @deprecated (since 7.3) Use {@linkplain Session#findByNaturalId} instead. */ +@Deprecated public interface SimpleNaturalIdLoadAccess { /** diff --git a/hibernate-core/src/main/java/org/hibernate/Timeouts.java b/hibernate-core/src/main/java/org/hibernate/Timeouts.java index ddeae2940271..c96315975244 100644 --- a/hibernate-core/src/main/java/org/hibernate/Timeouts.java +++ b/hibernate-core/src/main/java/org/hibernate/Timeouts.java @@ -146,4 +146,17 @@ static int fromHint(Object factoryHint) { } return Integer.parseInt( factoryHint.toString() ); } + + static Timeout fromHintTimeout(Object factoryHint) { + if ( factoryHint == null ) { + return WAIT_FOREVER; + } + if ( factoryHint instanceof Timeout timeout ) { + return timeout; + } + if ( factoryHint instanceof Integer number ) { + return Timeout.milliseconds( number ); + } + return Timeout.milliseconds( Integer.parseInt( factoryHint.toString() ) ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/NaturalId.java b/hibernate-core/src/main/java/org/hibernate/annotations/NaturalId.java index 4eca9fc13bab..6cfb11a3ada9 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/NaturalId.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/NaturalId.java @@ -4,6 +4,7 @@ */ package org.hibernate.annotations; + import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -11,80 +12,87 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Specifies that a field or property of an entity class is part of - * the natural id of the entity. This annotation is very useful when - * the primary key of an entity class is a surrogate key, that is, - * a {@linkplain jakarta.persistence.GeneratedValue system-generated} - * synthetic identifier, with no domain-model semantics. There should - * always be some other field or combination of fields which uniquely - * identifies an instance of the entity from the point of view of the - * user of the system. This is the natural id of the entity. - *

- * A natural id may be a single field or property of the entity: - *

- * @Entity
- * @Cache @NaturalIdCache
- * class Person {
- *
- *     //synthetic id
- *     @GeneratedValue @Id
- *     Long id;
- *
- *     @NotNull
- *     String name;
- *
- *     //simple natural id
- *     @NotNull @NaturalId
- *     String ssn;
- *
- *     ...
- * }
- * 
- *

- * or it may be a composite value: - *

- * @Entity
- * @Cache @NaturalIdCache
- * class Vehicle {
- *
- *     //synthetic id
- *     @GeneratedValue @Id
- *     Long id;
- *
- *     //composite natural id
- *
- *     @Enumerated
- *     @NotNull @NaturalId
- *     Region region;
- *
- *     @NotNull @NaturalId
- *     String registration;
- *
- *     ...
- * }
- * 
- *

- * Unlike the {@linkplain jakarta.persistence.Id primary identifier} - * of an entity, a natural id may be {@linkplain #mutable}. - *

- * On the other hand, a field or property which forms part of a natural - * id may never be null, and so it's a good idea to use {@code @NaturalId} - * in conjunction with the Bean Validation {@code @NotNull} annotation - * or {@link jakarta.persistence.Basic#optional @Basic(optional=false)}. - *

- * The {@link org.hibernate.Session} interface offers several methods - * that allow an entity instance to be retrieved by its - * {@linkplain org.hibernate.Session#bySimpleNaturalId(Class) simple} - * or {@linkplain org.hibernate.Session#byNaturalId(Class) composite} - * natural id value. If the entity is also marked for {@linkplain - * NaturalIdCache natural id caching}, then these methods may be able - * to avoid a database round trip. - * - * @author Nicolás Lichtmaier - * - * @see NaturalIdCache - */ +/// Specifies that a field or property of an entity class is part of +/// the natural id of the entity. This annotation is very useful when +/// the primary key of an entity class is a surrogate key, that is, +/// a {@linkplain jakarta.persistence.GeneratedValue system-generated} +/// synthetic identifier, with no domain-model semantics. There should +/// always be some other field or combination of fields which uniquely +/// identifies an instance of the entity from the point of view of the +/// user of the system. This is the _natural id_ of the entity. +/// +/// A natural id may be a single (basic or embedded) attribute of the entity: +/// ````java +/// @Entity +/// class Person { +/// +/// //synthetic id +/// @GeneratedValue @Id +/// Long id; +/// +/// @NotNull +/// String name; +/// +/// //simple natural id +/// @NotNull @NaturalId +/// String ssn; +/// +/// ... +/// } +/// ``` +/// +/// or it may be a non-aggregated composite value: +/// ```java +/// @Entity +/// class Vehicle { +/// +/// //synthetic id +/// @GeneratedValue @Id +/// Long id; +/// +/// //composite natural id +/// +/// @Enumerated +/// @NotNull +/// @NaturalId +/// Region region; +/// +/// @NotNull +/// @NaturalId +/// String registration; +/// +/// ... +/// } +/// ``` +/// +/// Unlike the {@linkplain jakarta.persistence.Id primary identifier} +/// of an entity, a natural id may be {@linkplain #mutable}. +/// +/// On the other hand, a field or property which forms part of a natural +/// id may never be null, and so it's a good idea to use `@NaturalId` +/// in conjunction with the Bean Validation `@NotNull` annotation +/// or [@Basic(optional=false)][jakarta.persistence.Basic#optional()]. +/// +/// The [org.hibernate.Session] interface offers several methods +/// that allow retrieval of one or more entity references by natural-id +/// allow an entity instance to be retrieved by its natural-id: +/// * [org.hibernate.Session#findByNaturalId] allows loading a single +/// entity instance by natural-id. +/// * [org.hibernate.Session#findMultipleByNaturalId] allows loading multiple +/// entity instances by natural-id. +/// +/// If the entity is also marked for [natural id caching][NaturalIdCache], +/// then these methods may be able to avoid a database round trip. +/// +/// @see org.hibernate.Session#findByNaturalId +/// @see org.hibernate.Session#findMultipleByNaturalId +/// @see NaturalIdClass +/// @see NaturalIdCache +/// +/// @apiNote For non-aggregated composite natural-id cases, it is recommended to +/// leverage [@NaturalIdClass][NaturalIdClass] for loading. +/// +/// @author Nicolás Lichtmaier @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface NaturalId { diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/NaturalIdClass.java b/hibernate-core/src/main/java/org/hibernate/annotations/NaturalIdClass.java new file mode 100644 index 000000000000..eafb1a61fc86 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/annotations/NaturalIdClass.java @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.Retention; + +/// Models a non-aggregated composite natural-id for the purpose of loading. +/// The non-aggregated form uses multiple [@NaturalId][NaturalId] as opposed +/// to the aggregated form which uses a single [@NaturalId][NaturalId] combined +/// with [@Embedded][jakarta.persistence.Embedded]. +/// Functions in a similar fashion as [@IdClass][jakarta.persistence.IdClass] for +/// non-aggregated composite identifiers. +/// +/// ```java +/// @Entity +/// @NaturalIdClass(OrderNaturalId.class) +/// class Order { +/// @Id +/// Integer id; +/// @NaturalId @ManyToOne +/// Customer customer; +/// @NaturalId +/// Integer orderNumber; +/// ... +/// } +/// +/// class OrderNaturalId { +/// Customer customer; +/// Integer orderNumber; +/// ... +/// } +/// ``` +/// +/// @see NaturalId +/// @see jakarta.persistence.IdClass +/// +/// @since 7.3 +/// +/// @author Steve Ebersole +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface NaturalIdClass { + /// The class to use for loading the associated entity by natural-id. + Class value(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java index e553f89e1c2b..607a8c53567e 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java @@ -29,7 +29,40 @@ import org.hibernate.AnnotationException; import org.hibernate.AssertionFailure; import org.hibernate.MappingException; -import org.hibernate.annotations.*; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.CacheLayout; +import org.hibernate.annotations.Check; +import org.hibernate.annotations.Checks; +import org.hibernate.annotations.ConcreteProxy; +import org.hibernate.annotations.DiscriminatorFormula; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.annotations.Filter; +import org.hibernate.annotations.Filters; +import org.hibernate.annotations.HQLSelect; +import org.hibernate.annotations.Immutable; +import org.hibernate.annotations.Mutability; +import org.hibernate.annotations.NaturalIdCache; +import org.hibernate.annotations.NaturalIdClass; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OptimisticLockType; +import org.hibernate.annotations.OptimisticLocking; +import org.hibernate.annotations.QueryCacheLayout; +import org.hibernate.annotations.RowId; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLDeleteAll; +import org.hibernate.annotations.SQLInsert; +import org.hibernate.annotations.SQLRestriction; +import org.hibernate.annotations.SQLSelect; +import org.hibernate.annotations.SQLUpdate; +import org.hibernate.annotations.SecondaryRow; +import org.hibernate.annotations.SecondaryRows; +import org.hibernate.annotations.SoftDelete; +import org.hibernate.annotations.Subselect; +import org.hibernate.annotations.Synchronize; +import org.hibernate.annotations.TypeBinderType; +import org.hibernate.annotations.View; import org.hibernate.binder.TypeBinder; import org.hibernate.boot.model.NamedEntityGraphDefinition; import org.hibernate.boot.model.internal.InheritanceState.ElementsToProcess; @@ -86,6 +119,7 @@ import static jakarta.persistence.InheritanceType.SINGLE_TABLE; import static java.util.Collections.addAll; +import static org.hibernate.boot.BootLogging.BOOT_LOGGER; import static org.hibernate.boot.model.internal.AnnotatedClassType.MAPPED_SUPERCLASS; import static org.hibernate.boot.model.internal.AnnotatedDiscriminatorColumn.DEFAULT_DISCRIMINATOR_COLUMN_NAME; import static org.hibernate.boot.model.internal.AnnotatedDiscriminatorColumn.buildDiscriminatorColumn; @@ -112,7 +146,6 @@ import static org.hibernate.boot.spi.AccessType.getAccessStrategy; import static org.hibernate.engine.OptimisticLockStyle.fromLockType; import static org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle.fromResultCheckStyle; -import static org.hibernate.boot.BootLogging.BOOT_LOGGER; import static org.hibernate.internal.util.ReflectHelper.getDefaultSupplier; import static org.hibernate.internal.util.StringHelper.isBlank; import static org.hibernate.internal.util.StringHelper.isNotBlank; @@ -159,6 +192,7 @@ public class EntityBinder { private String cacheRegion; private boolean cacheLazyProperty; private String naturalIdCacheRegion; + private ClassDetails naturalIdClass; private CacheLayout queryCacheLayout; private ModelsContext modelsContext() { @@ -1264,6 +1298,7 @@ private void bindEntity() { bindSqlRestriction(); bindCache(); bindNaturalIdCache(); + bindNaturalIdClass(); bindFiltersInHierarchy(); persistentClass.setAbstract( annotatedClass.isAbstract() ); @@ -1338,6 +1373,7 @@ private void bindRootEntity() { rootClass.setLazyPropertiesCacheable( cacheLazyProperty ); } rootClass.setNaturalIdCacheRegionName( naturalIdCacheRegion ); + rootClass.setNaturalIdClass( naturalIdClass ); } private void bindCustomSql() { @@ -1613,6 +1649,20 @@ private SQLRestriction extractSQLRestriction(ClassDetails classDetails) { return null; } + private void bindNaturalIdClass() { + final var ann = annotatedClass.getAnnotationUsage( NaturalIdClass.class, modelsContext() ); + if ( ann != null ) { + if ( ann.value() == void.class ) { + throw new IllegalStateException( "NaturalIdClass#value must not be void.class" ); + } + naturalIdClass = context + .getBootstrapContext() + .getModelsContext() + .getClassDetailsRegistry() + .resolveClassDetails( ann.value().getName() ); + } + } + private void bindNaturalIdCache() { final var naturalIdCache = annotatedClass.getAnnotationUsage( NaturalIdCache.class, modelsContext() ); if ( naturalIdCache != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/HibernateAnnotations.java b/hibernate-core/src/main/java/org/hibernate/boot/models/HibernateAnnotations.java index 7c343f3bac4b..08f5522c47c1 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/HibernateAnnotations.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/HibernateAnnotations.java @@ -475,6 +475,10 @@ public interface HibernateAnnotations { NaturalIdCache.class, NaturalIdCacheAnnotation.class ); + OrmAnnotationDescriptor NATURAL_ID_CLASS = new OrmAnnotationDescriptor<>( + NaturalIdClass.class, + NaturalIdClassAnnotation.class + ); OrmAnnotationDescriptor NOT_FOUND = new OrmAnnotationDescriptor<>( NotFound.class, NotFoundAnnotation.class diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/annotations/internal/NaturalIdClassAnnotation.java b/hibernate-core/src/main/java/org/hibernate/boot/models/annotations/internal/NaturalIdClassAnnotation.java new file mode 100644 index 000000000000..654b5e292dc6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/annotations/internal/NaturalIdClassAnnotation.java @@ -0,0 +1,53 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.boot.models.annotations.internal; + +import org.hibernate.annotations.NaturalIdClass; +import org.hibernate.models.spi.ModelsContext; + +import java.lang.annotation.Annotation; +import java.util.Map; + +@SuppressWarnings({ "ClassExplicitlyAnnotation", "unused" }) +@jakarta.annotation.Generated("org.hibernate.orm.build.annotations.ClassGeneratorProcessor") +public class NaturalIdClassAnnotation implements NaturalIdClass { + private Class value; + + /** + * Used in creating dynamic annotation instances (e.g. from XML) + */ + public NaturalIdClassAnnotation(ModelsContext modelContext) { + this.value = void.class; + } + + /** + * Used in creating annotation instances from JDK variant + */ + public NaturalIdClassAnnotation(NaturalIdClass annotation, ModelsContext modelContext) { + this.value = annotation.value(); + } + + /** + * Used in creating annotation instances from Jandex variant + */ + public NaturalIdClassAnnotation(Map attributeValues, ModelsContext modelContext) { + this.value = (Class) attributeValues.get( "value" ); + } + + @Override + public Class annotationType() { + return NaturalIdClass.class; + } + + @Override + public Class value() { + return value; + } + + public void value(Class value) { + this.value = value; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/NumberSeriesGenerateSeriesFunction.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/NumberSeriesGenerateSeriesFunction.java index 2633d0acbd5e..b2054ee942ac 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/NumberSeriesGenerateSeriesFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/NumberSeriesGenerateSeriesFunction.java @@ -48,7 +48,7 @@ import org.hibernate.sql.ast.tree.predicate.PredicateContainer; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SelectStatement; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.type.BasicType; import org.hibernate.type.SqlTypes; @@ -458,10 +458,10 @@ private static String timestampadd(String startExpression, String stepExpression type ) ) ); - final SqlAstTranslator translator = + final SqlAstTranslator translator = creationContext.getDialect().getSqlAstTranslatorFactory() .buildSelectTranslator( creationContext.getSessionFactory(), new SelectStatement( fakeQuery ) ); - final JdbcOperationQuerySelect operation = translator.translate( null, QueryOptions.NONE ); + final JdbcSelect operation = translator.translate( null, QueryOptions.NONE ); final String sqlString = operation.getSqlString(); assert sqlString.startsWith( "select " ); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/MultiIdentifierLoadAccessImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/MultiIdentifierLoadAccessImpl.java index e2fdedb0f9d1..10b05f7fcc7f 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/MultiIdentifierLoadAccessImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/MultiIdentifierLoadAccessImpl.java @@ -7,11 +7,15 @@ import jakarta.persistence.EntityGraph; import jakarta.persistence.PessimisticLockScope; import jakarta.persistence.Timeout; +import org.hibernate.BatchSize; import org.hibernate.CacheMode; +import org.hibernate.KeyType; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.MultiIdentifierLoadAccess; +import org.hibernate.NaturalIdSynchronization; import org.hibernate.OrderingMode; +import org.hibernate.ReadOnlyMode; import org.hibernate.RemovalsMode; import org.hibernate.SessionCheckMode; import org.hibernate.UnknownProfileException; @@ -19,19 +23,23 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.internal.find.FindMultipleByKeyOperation; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; +import org.hibernate.loader.internal.LoadAccessContext; import org.hibernate.persister.entity.EntityPersister; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.function.Supplier; import static java.util.Collections.emptyList; -/** - * @author Steve Ebersole - */ +/// Implementation of MultiIdentifierLoadAccess. +/// +/// @author Steve Ebersole +/// +/// @deprecated Use [FindMultipleByKeyOperation] instead. +@Deprecated class MultiIdentifierLoadAccessImpl implements MultiIdentifierLoadAccess, MultiIdLoadOptions { private final SharedSessionContractImplementor session; private final EntityPersister entityPersister; @@ -43,7 +51,7 @@ class MultiIdentifierLoadAccessImpl implements MultiIdentifierLoadAccess, private RootGraphImplementor rootGraph; private GraphSemantic graphSemantic; - private Integer batchSize; + private BatchSize batchSize; private SessionCheckMode sessionCheckMode = SessionCheckMode.DISABLED; private RemovalsMode removalsMode = RemovalsMode.REPLACE; protected OrderingMode orderingMode = OrderingMode.ORDERED; @@ -107,12 +115,12 @@ public MultiIdentifierLoadAccess with(EntityGraph graph, GraphSemantic sem @Override public Integer getBatchSize() { - return batchSize; + return batchSize.batchSize(); } @Override public MultiIdentifierLoadAccess withBatchSize(int batchSize) { - this.batchSize = batchSize < 1 ? null : batchSize; + this.batchSize = batchSize < 1 ? null : new BatchSize( batchSize ); return this; } @@ -164,45 +172,25 @@ public Boolean getReadOnly(SessionImplementor session) { @Override @SuppressWarnings( "unchecked" ) public List multiLoad(K... ids) { - return perform( () -> (List) entityPersister.multiLoad( ids, session, this ) ); - } - - public List perform(Supplier> executor) { - final var sessionCacheMode = session.getCacheMode(); - boolean cacheModeChanged = false; - if ( cacheMode != null ) { - // naive check for now... - // todo : account for "conceptually equal" - if ( cacheMode != sessionCacheMode ) { - session.setCacheMode( cacheMode ); - cacheModeChanged = true; - } - } + return buildOperation().performFind( List.of( ids ), graphSemantic, rootGraph, (LoadAccessContext) session ); + } - try { - final var influencers = session.getLoadQueryInfluencers(); - final var fetchProfiles = - influencers.adjustFetchProfiles( disabledFetchProfiles, enabledFetchProfiles ); - final var effectiveEntityGraph = - rootGraph == null - ? null - : influencers.applyEntityGraph( rootGraph, graphSemantic ); - try { - return executor.get(); - } - finally { - if ( effectiveEntityGraph != null ) { - effectiveEntityGraph.clear(); - } - influencers.setEnabledFetchProfileNames( fetchProfiles ); - } - } - finally { - if ( cacheModeChanged ) { - // change it back - session.setCacheMode( sessionCacheMode ); - } - } + private FindMultipleByKeyOperation buildOperation() { + return new FindMultipleByKeyOperation( + entityPersister, + KeyType.IDENTIFIER, + batchSize, + sessionCheckMode, + removalsMode, + orderingMode, + cacheMode, + lockOptions, + readOnly == Boolean.TRUE ? ReadOnlyMode.READ_ONLY : ReadOnlyMode.READ_WRITE, + enabledFetchProfiles, + disabledFetchProfiles, + // irrelevant for load-by-id + NaturalIdSynchronization.DISABLED + ); } @Override @@ -210,7 +198,7 @@ public List perform(Supplier> executor) { public List multiLoad(List ids) { return ids.isEmpty() ? emptyList() - : perform( () -> (List) entityPersister.multiLoad( ids.toArray(), session, this ) ); + : buildOperation().performFind( (List)ids, graphSemantic, rootGraph, (LoadAccessContext) session ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdHelper.java index b607a9f59997..b9cde2b804f3 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdHelper.java @@ -43,7 +43,7 @@ public static void performAnyNeededCrossReferenceSynchronizations( // first check if synchronization (this process) was disabled if ( synchronizationEnabled // only mutable natural-ids need this processing - && entityMappingType.getNaturalIdMapping().isMutable() + && entityMappingType.requireNaturalIdMapping().isMutable() // skip synchronization when not in a transaction && session.isTransactionInProgress() ) { final var persister = entityMappingType.getEntityPersister(); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdMultiLoadAccessStandard.java b/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdMultiLoadAccessStandard.java index bb84eb30e4e6..99c42d25957e 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdMultiLoadAccessStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/NaturalIdMultiLoadAccessStandard.java @@ -4,30 +4,38 @@ */ package org.hibernate.internal; -import java.util.List; - import jakarta.persistence.EntityGraph; - import jakarta.persistence.PessimisticLockScope; import jakarta.persistence.Timeout; +import org.hibernate.BatchSize; import org.hibernate.CacheMode; +import org.hibernate.KeyType; import org.hibernate.LockMode; import org.hibernate.LockOptions; +import org.hibernate.Locking; import org.hibernate.NaturalIdMultiLoadAccess; +import org.hibernate.NaturalIdSynchronization; import org.hibernate.OrderingMode; +import org.hibernate.ReadOnlyMode; import org.hibernate.RemovalsMode; +import org.hibernate.SessionCheckMode; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.internal.find.FindMultipleByKeyOperation; import org.hibernate.loader.ast.spi.MultiNaturalIdLoadOptions; +import org.hibernate.loader.internal.LoadAccessContext; import org.hibernate.persister.entity.EntityPersister; -import static org.hibernate.internal.NaturalIdHelper.performAnyNeededCrossReferenceSynchronizations; +import java.util.List; -/** - * @author Steve Ebersole - */ -class NaturalIdMultiLoadAccessStandard implements NaturalIdMultiLoadAccess, MultiNaturalIdLoadOptions { +/// Implementation of NaturalIdMultiLoadAccess. +/// +/// @deprecated Use [FindMultipleByKeyOperation] instead. +/// +/// @author Steve Ebersole +@Deprecated +public class NaturalIdMultiLoadAccessStandard implements NaturalIdMultiLoadAccess, MultiNaturalIdLoadOptions { private final EntityPersister entityDescriptor; private final SharedSessionContractImplementor session; @@ -41,7 +49,7 @@ class NaturalIdMultiLoadAccessStandard implements NaturalIdMultiLoadAccess private RemovalsMode removalsMode = RemovalsMode.REPLACE; private OrderingMode orderingMode = OrderingMode.ORDERED; - NaturalIdMultiLoadAccessStandard(EntityPersister entityDescriptor, SharedSessionContractImplementor session) { + public NaturalIdMultiLoadAccessStandard(EntityPersister entityDescriptor, SharedSessionContractImplementor session) { this.entityDescriptor = entityDescriptor; this.session = session; } @@ -56,6 +64,13 @@ public NaturalIdMultiLoadAccess with(LockMode lockMode, PessimisticLockScope return this; } + public void with(Locking.Scope scope) { + if ( lockOptions == null ) { + lockOptions = new LockOptions(); + } + lockOptions.setScope( scope ); + } + @Override public NaturalIdMultiLoadAccess with(Timeout timeout) { if ( lockOptions == null ) { @@ -96,72 +111,48 @@ public NaturalIdMultiLoadAccess enableReturnOfDeletedEntities(boolean enabled return this; } + public void with(RemovalsMode removalsMode) { + this.removalsMode = removalsMode; + } + @Override public NaturalIdMultiLoadAccess enableOrderedReturn(boolean enabled) { this.orderingMode = enabled ? OrderingMode.ORDERED : OrderingMode.UNORDERED; return this; } + public void with(OrderingMode orderingMode) { + this.orderingMode = orderingMode; + } + @Override @SuppressWarnings( "unchecked" ) public List multiLoad(Object... ids) { - performAnyNeededCrossReferenceSynchronizations( true, entityDescriptor, session ); - - final CacheMode sessionCacheMode = session.getCacheMode(); - boolean cacheModeChanged = false; - - if ( cacheMode != null ) { - // naive check for now... - // todo : account for "conceptually equal" - if ( cacheMode != sessionCacheMode ) { - session.setCacheMode( cacheMode ); - cacheModeChanged = true; - } - } - - final var loadQueryInfluencers = session.getLoadQueryInfluencers(); - - try { - final var effectiveEntityGraph = loadQueryInfluencers.getEffectiveEntityGraph(); - final var initialGraphSemantic = effectiveEntityGraph.getSemantic(); - final var initialGraph = effectiveEntityGraph.getGraph(); - final boolean hadInitialGraph = initialGraphSemantic != null; - - if ( graphSemantic != null ) { - if ( rootGraph == null ) { - throw new IllegalArgumentException( "Graph semantic specified, but no RootGraph was supplied" ); - } - effectiveEntityGraph.applyGraph( rootGraph, graphSemantic ); - } - - try { - return (List) - entityDescriptor.getMultiNaturalIdLoader() - .multiLoad( ids, this, session ); - } - finally { - if ( graphSemantic != null ) { - if ( hadInitialGraph ) { - effectiveEntityGraph.applyGraph( initialGraph, initialGraphSemantic ); - } - else { - effectiveEntityGraph.clear(); - } - } - } - } - finally { - if ( cacheModeChanged ) { - // change it back - session.setCacheMode( sessionCacheMode ); - } - } - + return buildOperation() + .performFind( List.of( ids ), graphSemantic, rootGraph, (LoadAccessContext) session ); } @Override public List multiLoad(List ids) { - return multiLoad( ids.toArray( new Object[ 0 ] ) ); + return buildOperation() + .performFind( (List) ids, graphSemantic, rootGraph, (LoadAccessContext) session ); + } + + private FindMultipleByKeyOperation buildOperation() { + return new FindMultipleByKeyOperation( + entityDescriptor, + KeyType.NATURAL, + batchSize == null ? null : new BatchSize( batchSize ), + SessionCheckMode.ENABLED, + removalsMode, + orderingMode, + cacheMode, + lockOptions, + session.isDefaultReadOnly() ? ReadOnlyMode.READ_ONLY : ReadOnlyMode.READ_WRITE, + null, + null, + NaturalIdSynchronization.ENABLED + ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 47f6e75468e8..887fa69afc1f 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -41,8 +41,9 @@ import org.hibernate.event.spi.*; import org.hibernate.event.spi.LoadEventListener.LoadType; import org.hibernate.graph.GraphSemantic; -import org.hibernate.graph.RootGraph; import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.internal.find.FindByKeyOperation; +import org.hibernate.internal.find.FindMultipleByKeyOperation; import org.hibernate.internal.util.ExceptionHelper; import org.hibernate.jpa.internal.LegacySpecHelper; import org.hibernate.jpa.internal.util.ConfigurationHelper; @@ -931,80 +932,46 @@ public void load(Object object, Object id) { fireLoad( new LoadEvent( id, object, this, getReadOnlyFromLoadQueryInfluencers() ), LoadEventListener.RELOAD ); } - private void setMultiIdentifierLoadAccessOptions(FindOption[] options, MultiIdentifierLoadAccess loadAccess) { - CacheStoreMode storeMode = getCacheStoreMode(); - CacheRetrieveMode retrieveMode = getCacheRetrieveMode(); - LockOptions lockOptions = copySessionLockOptions(); - int batchSize = -1; - for ( var option : options ) { - if ( option instanceof CacheStoreMode cacheStoreMode ) { - storeMode = cacheStoreMode; - } - else if ( option instanceof CacheRetrieveMode cacheRetrieveMode ) { - retrieveMode = cacheRetrieveMode; - } - else if ( option instanceof CacheMode cacheMode ) { - storeMode = cacheMode.getJpaStoreMode(); - retrieveMode = cacheMode.getJpaRetrieveMode(); - } - else if ( option instanceof LockModeType lockModeType ) { - lockOptions.setLockMode( LockModeTypeHelper.getLockMode( lockModeType ) ); - } - else if ( option instanceof LockMode lockMode ) { - lockOptions.setLockMode( lockMode ); - } - else if ( option instanceof LockOptions lockOpts ) { - lockOptions = lockOpts; - } - else if ( option instanceof PessimisticLockScope pessimisticLockScope ) { - lockOptions.setLockScope( pessimisticLockScope ); - } - else if ( option instanceof Timeout timeout ) { - lockOptions.setTimeOut( timeout.milliseconds() ); - } - else if ( option instanceof EnabledFetchProfile enabledFetchProfile ) { - loadAccess.enableFetchProfile( enabledFetchProfile.profileName() ); - } - else if ( option instanceof ReadOnlyMode ) { - loadAccess.withReadOnly( option == ReadOnlyMode.READ_ONLY ); - } - else if ( option instanceof BatchSize batchSizeOption ) { - batchSize = batchSizeOption.batchSize(); - } - else if ( option instanceof SessionCheckMode ) { - loadAccess.enableSessionCheck( option == SessionCheckMode.ENABLED ); - } - else if ( option instanceof OrderingMode ) { - loadAccess.enableOrderedReturn( option == OrderingMode.ORDERED ); - } - else if ( option instanceof RemovalsMode ) { - loadAccess.enableReturnOfDeletedEntities( option == RemovalsMode.INCLUDE ); - } - } - loadAccess.with( lockOptions ) - .with( interpretCacheMode( storeMode, retrieveMode ) ) - .withBatchSize( batchSize ); + @Override + public List findMultiple(Class entityType, List keys, FindOption... options) { + //noinspection unchecked + return findMultiple( + requireEntityPersister( entityType ), + loadQueryInfluencers.getEffectiveEntityGraph().getSemantic(), + (RootGraphImplementor) loadQueryInfluencers.getEffectiveEntityGraph().getGraph(), + (List) keys, + options + ); } - @Override - public List findMultiple(Class entityType, List ids, FindOption... options) { - final var loadAccess = byMultipleIds( entityType ); - setMultiIdentifierLoadAccessOptions( options, loadAccess ); - return loadAccess.multiLoad( ids ); + private List findMultiple( + EntityPersister entityDescriptor, + GraphSemantic graphSemantic, + RootGraphImplementor rootGraph, + List keys, + FindOption... options) { + final var operation = new FindMultipleByKeyOperation( + entityDescriptor, + lockOptions, + getCacheMode(), + isDefaultReadOnly(), + getFactory(), + options + ); + return operation.performFind( keys, graphSemantic, rootGraph, this ); } @Override - public List findMultiple(EntityGraph entityGraph, List ids, FindOption... options) { - final var rootGraph = (RootGraph) entityGraph; + public List findMultiple(EntityGraph entityGraph, List keys, FindOption... options) { + final var rootGraph = (RootGraphImplementor) entityGraph; final var type = rootGraph.getGraphedType(); - final MultiIdentifierLoadAccess loadAccess = - switch ( type.getRepresentationMode() ) { - case MAP -> byMultipleIds( type.getTypeName() ); - case POJO -> byMultipleIds( type.getJavaType() ); - }; - loadAccess.withLoadGraph( rootGraph ); - setMultiIdentifierLoadAccessOptions( options, loadAccess ); - return loadAccess.multiLoad( ids ); + final var entityDescriptor = switch ( type.getRepresentationMode() ) { + case POJO -> requireEntityPersister( type.getJavaType() ); + case MAP -> requireEntityPersister( type.getTypeName() ); + }; + + //noinspection unchecked + return findMultiple( entityDescriptor, GraphSemantic.LOAD, rootGraph, (List) keys, options ); } @Override @@ -2256,81 +2223,24 @@ protected static void logIgnoringEntityNotFound(Class entityClass, Object } } - private void setLoadAccessOptions(FindOption[] options, IdentifierLoadAccessImpl loadAccess) { - CacheStoreMode storeMode = getCacheStoreMode(); - CacheRetrieveMode retrieveMode = getCacheRetrieveMode(); - LockOptions lockOptions = copySessionLockOptions(); - for ( var option : options ) { - if ( option instanceof CacheStoreMode cacheStoreMode ) { - storeMode = cacheStoreMode; - } - else if ( option instanceof CacheRetrieveMode cacheRetrieveMode ) { - retrieveMode = cacheRetrieveMode; - } - else if ( option instanceof CacheMode cacheMode ) { - storeMode = cacheMode.getJpaStoreMode(); - retrieveMode = cacheMode.getJpaRetrieveMode(); - } - else if ( option instanceof LockModeType lockModeType ) { - lockOptions.setLockMode( LockModeTypeHelper.getLockMode( lockModeType ) ); - } - else if ( option instanceof LockMode lockMode ) { - lockOptions.setLockMode( lockMode ); - } - else if ( option instanceof LockOptions lockOpts ) { - lockOptions = lockOpts; - } - else if ( option instanceof Locking.Scope lockScope ) { - lockOptions.setScope( lockScope ); - } - else if ( option instanceof PessimisticLockScope pessimisticLockScope ) { - lockOptions.setScope( Locking.Scope.fromJpaScope( pessimisticLockScope ) ); - } - else if ( option instanceof Locking.FollowOn followOn ) { - lockOptions.setFollowOnStrategy( followOn ); - } - else if ( option instanceof Timeout timeout ) { - lockOptions.setTimeout( timeout ); - } - else if ( option instanceof EnabledFetchProfile enabledFetchProfile ) { - loadAccess.enableFetchProfile( enabledFetchProfile.profileName() ); - } - else if ( option instanceof ReadOnlyMode ) { - loadAccess.withReadOnly( option == ReadOnlyMode.READ_ONLY ); - } - else if ( option instanceof FindMultipleOption findMultipleOption ) { - throw new IllegalArgumentException( "Option '" + findMultipleOption - + "' can only be used in 'findMultiple()'" ); - } - } - if ( lockOptions.getLockMode().isPessimistic() - && lockOptions.getTimeOut() == WAIT_FOREVER_MILLI ) { - final Object factoryHint = getFactory().getProperties().get( HINT_SPEC_LOCK_TIMEOUT ); - if ( factoryHint != null ) { - lockOptions.setTimeOut( Timeouts.fromHint( factoryHint ) ); - } - } - loadAccess.with( lockOptions ).with( interpretCacheMode( storeMode, retrieveMode ) ); - } - @Override - public T find(Class entityClass, Object primaryKey, FindOption... options) { - final IdentifierLoadAccessImpl loadAccess = byId( entityClass ); - setLoadAccessOptions( options, loadAccess ); - return loadAccess.load( primaryKey ); + public T find(Class entityClass, Object key, FindOption... options) { + //noinspection unchecked + return (T) byKey( requireEntityPersister( entityClass ), options ).performFind( key, this ); } @Override - public T find(EntityGraph entityGraph, Object primaryKey, FindOption... options) { - final var graph = (RootGraph) entityGraph; + public T find(EntityGraph entityGraph, Object key, FindOption... options) { + final var graph = (RootGraphImplementor) entityGraph; final var type = graph.getGraphedType(); - final IdentifierLoadAccessImpl loadAccess = - switch ( type.getRepresentationMode() ) { - case MAP -> byId( type.getTypeName() ); - case POJO -> byId( type.getJavaType() ); - }; - setLoadAccessOptions( options, loadAccess ); - return loadAccess.withLoadGraph( graph ).load( primaryKey ); + + final EntityPersister entityDescriptor = switch ( type.getRepresentationMode() ) { + case POJO -> requireEntityPersister( type.getJavaType() ); + case MAP -> requireEntityPersister( type.getTypeName() ); + }; + + //noinspection unchecked + return (T) byKey( entityDescriptor, GraphSemantic.LOAD, graph, options ).performFind( key, this ); } // Hibernate Reactive may need to use this @@ -2393,16 +2303,34 @@ private void checkTransactionNeededForUpdateOperation() { } @Override - public Object find(String entityName, Object primaryKey) { - final IdentifierLoadAccessImpl loadAccess = byId( entityName ); - return loadAccess.load( primaryKey ); + public Object find(String entityName, Object key) { + return byKey( requireEntityPersister( entityName ) ).performFind( key, this ); } @Override - public Object find(String entityName, Object primaryKey, FindOption... options) { - final IdentifierLoadAccessImpl loadAccess = byId( entityName ); - setLoadAccessOptions( options, loadAccess ); - return loadAccess.load( primaryKey ); + public Object find(String entityName, Object key, FindOption... options) { + return byKey( requireEntityPersister( entityName ), options ).performFind( key, this ); + } + + private FindByKeyOperation byKey(EntityPersister entityDescriptor, FindOption... options) { + return byKey( entityDescriptor, null, null, options ); + } + + private FindByKeyOperation byKey( + EntityPersister entityDescriptor, + GraphSemantic graphSemantic, + RootGraphImplementor rootGraph, + FindOption... options) { + return new FindByKeyOperation<>( + entityDescriptor, + graphSemantic, + rootGraph, + lockOptions, + getCacheMode(), + isReadOnly(), + getFactory(), + options + ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/internal/find/FindByKeyOperation.java b/hibernate-core/src/main/java/org/hibernate/internal/find/FindByKeyOperation.java new file mode 100644 index 000000000000..ab27646b3bf9 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/internal/find/FindByKeyOperation.java @@ -0,0 +1,338 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.internal.find; + +import jakarta.persistence.CacheRetrieveMode; +import jakarta.persistence.CacheStoreMode; +import jakarta.persistence.FindOption; +import jakarta.persistence.LockModeType; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.CacheMode; +import org.hibernate.EnabledFetchProfile; +import org.hibernate.KeyType; +import org.hibernate.FindMultipleOption; +import org.hibernate.LockMode; +import org.hibernate.LockOptions; +import org.hibernate.Locking; +import org.hibernate.NaturalIdSynchronization; +import org.hibernate.ObjectNotFoundException; +import org.hibernate.ReadOnlyMode; +import org.hibernate.Timeouts; +import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.Status; +import org.hibernate.event.spi.LoadEventListener; +import org.hibernate.graph.GraphSemantic; +import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.jpa.internal.util.LockModeTypeHelper; +import org.hibernate.loader.ast.spi.NaturalIdLoader; +import org.hibernate.loader.internal.LoadAccessContext; +import org.hibernate.metamodel.mapping.NaturalIdMapping; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.proxy.HibernateProxy; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; + +import static org.hibernate.Timeouts.WAIT_FOREVER; +import static org.hibernate.engine.spi.NaturalIdResolutions.INVALID_NATURAL_ID_REFERENCE; +import static org.hibernate.internal.NaturalIdHelper.performAnyNeededCrossReferenceSynchronizations; +import static org.hibernate.jpa.SpecHints.HINT_SPEC_LOCK_TIMEOUT; +import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; + +/// Support for loading a single entity by key (either [id][KeyType#IDENTIFIER] or [natural-id][KeyType#NATURAL]). +/// +/// @see org.hibernate.Session#find +/// @see KeyType +/// +/// @author Steve Ebersole +public class FindByKeyOperation implements NaturalIdLoader.Options { + private final EntityPersister entityDescriptor; + + private KeyType keyType = KeyType.IDENTIFIER; + + private CacheStoreMode cacheStoreMode; + private CacheRetrieveMode cacheRetrieveMode; + + private LockMode lockMode; + private Locking.Scope lockScope; + private Locking.FollowOn lockFollowOn; + private Timeout lockTimeout = WAIT_FOREVER; + + private ReadOnlyMode readOnlyMode; + + private RootGraphImplementor rootGraph; + private GraphSemantic graphSemantic; + + private Set enabledFetchProfiles; + + private NaturalIdSynchronization naturalIdSynchronization; + + public FindByKeyOperation( + @NonNull EntityPersister entityDescriptor, + @Nullable GraphSemantic graphSemantic, + @Nullable RootGraphImplementor rootGraph, + @Nullable LockOptions defaultLockOptions, + @Nullable CacheMode defaultCacheMode, + boolean defaultReadOnly, + @NonNull SessionFactoryImplementor sessionFactory, + FindOption... findOptions) { + this.entityDescriptor = entityDescriptor; + + this.graphSemantic = graphSemantic; + this.rootGraph = rootGraph; + + if ( defaultCacheMode != null ) { + cacheStoreMode = defaultCacheMode.getJpaStoreMode(); + cacheRetrieveMode = defaultCacheMode.getJpaRetrieveMode(); + } + + if ( defaultLockOptions != null ) { + lockMode = defaultLockOptions.getLockMode(); + lockScope = defaultLockOptions.getScope(); + lockTimeout = defaultLockOptions.getTimeout(); + lockFollowOn = defaultLockOptions.getFollowOnStrategy(); + } + if ( lockTimeout == WAIT_FOREVER ) { + final Object factoryTimeoutHint = sessionFactory.getProperties().get( HINT_SPEC_LOCK_TIMEOUT ); + if ( factoryTimeoutHint != null ) { + lockTimeout = Timeouts.fromHintTimeout( factoryTimeoutHint ); + } + } + + readOnlyMode = defaultReadOnly ? ReadOnlyMode.READ_ONLY : ReadOnlyMode.READ_WRITE; + + for ( FindOption option : findOptions ) { + if ( option instanceof KeyType keyType ) { + this.keyType = keyType; + } + else if ( option instanceof CacheStoreMode cacheStoreMode ) { + this.cacheStoreMode = cacheStoreMode; + } + else if ( option instanceof CacheRetrieveMode cacheRetrieveMode ) { + this.cacheRetrieveMode = cacheRetrieveMode; + } + else if ( option instanceof CacheMode cacheMode ) { + this.cacheStoreMode = cacheMode.getJpaStoreMode(); + this.cacheRetrieveMode = cacheMode.getJpaRetrieveMode(); + } + else if ( option instanceof LockModeType lockModeType ) { + this.lockMode = LockModeTypeHelper.getLockMode( lockModeType ); + } + else if ( option instanceof LockMode lockMode ) { + this.lockMode = lockMode; + } + else if ( option instanceof Locking.Scope lockScope ) { + this.lockScope = lockScope; + } + else if ( option instanceof PessimisticLockScope pessimisticLockScope ) { + this.lockScope = Locking.Scope.fromJpaScope( pessimisticLockScope ); + } + else if ( option instanceof Locking.FollowOn followOn ) { + this.lockFollowOn = followOn; + } + else if ( option instanceof Timeout timeout ) { + this.lockTimeout = timeout; + } + else if ( option instanceof ReadOnlyMode readOnlyMode) { + this.readOnlyMode = readOnlyMode; + } + else if ( option instanceof EnabledFetchProfile enabledFetchProfile ) { + this.enabledFetchProfile( enabledFetchProfile.profileName() ); + } + else if ( option instanceof NaturalIdSynchronization naturalIdSynchronization ) { + this.naturalIdSynchronization = naturalIdSynchronization; + } + else if ( option instanceof FindMultipleOption findMultipleOption ) { + throw new IllegalArgumentException( "Option '" + findMultipleOption + "' can only be used in 'findMultiple()'" ); + } + } + } + + private void enabledFetchProfile(String profileName) { + if ( enabledFetchProfiles == null ) { + enabledFetchProfiles = new HashSet<>(); + } + enabledFetchProfiles.add( profileName ); + } + + public T performFind(Object key, LoadAccessContext loadAccessContext) { + if ( keyType == KeyType.NATURAL ) { + return findByNaturalId( key, loadAccessContext ); + } + else { + return findById( key, loadAccessContext ); + } + + } + + private T findByNaturalId(Object key, LoadAccessContext loadAccessContext) { + final NaturalIdMapping naturalIdMapping = entityDescriptor.requireNaturalIdMapping(); + final SessionImplementor session = loadAccessContext.getSession(); + + performAnyNeededCrossReferenceSynchronizations( + naturalIdSynchronization != NaturalIdSynchronization.DISABLED, + entityDescriptor, + session + ); + + final var normalizedKey = naturalIdMapping.normalizeInput( key ); + + final Object cachedResolution = getCachedNaturalIdResolution( normalizedKey, loadAccessContext ); + if ( cachedResolution == INVALID_NATURAL_ID_REFERENCE ) { + return null; + } + + if ( cachedResolution != null ) { + return findById( cachedResolution, loadAccessContext ); + } + + return withOptions( loadAccessContext, () -> { + @SuppressWarnings("unchecked") + final T loaded = (T) entityDescriptor.getNaturalIdLoader() + .load( normalizedKey, this, session ); + if ( loaded != null ) { + final var persistenceContext = session.getPersistenceContextInternal(); + final var lazyInitializer = HibernateProxy.extractLazyInitializer( loaded ); + final var entity = lazyInitializer != null ? lazyInitializer.getImplementation() : loaded; + final var entry = persistenceContext.getEntry( entity ); + assert entry != null; + if ( entry.getStatus() == Status.DELETED ) { + return null; + } + } + return loaded; + } ); + } + + private T withOptions(LoadAccessContext loadAccessContext, Supplier action) { + final var session = loadAccessContext.getSession(); + final var influencers = session.getLoadQueryInfluencers(); + final var fetchProfiles = influencers.adjustFetchProfiles( null, enabledFetchProfiles ); + final var effectiveEntityGraph = rootGraph == null + ? null + : influencers.applyEntityGraph( rootGraph, graphSemantic ); + + final var readOnly = session.isDefaultReadOnly(); + session.setDefaultReadOnly( readOnlyMode == ReadOnlyMode.READ_ONLY ); + + final var cacheMode = session.getCacheMode(); + session.setCacheMode( CacheMode.fromJpaModes( cacheRetrieveMode, cacheStoreMode ) ); + + try { + return action.get(); + } + finally { + loadAccessContext.delayedAfterCompletion(); + if ( effectiveEntityGraph != null ) { + effectiveEntityGraph.clear(); + } + influencers.setEnabledFetchProfileNames( fetchProfiles ); + session.setDefaultReadOnly( readOnly ); + session.setCacheMode( cacheMode ); + } + } + + private Object getCachedNaturalIdResolution( + Object normalizedNaturalIdValue, + LoadAccessContext loadAccessContext) { + loadAccessContext.checkOpenOrWaitingForAutoClose(); + loadAccessContext.pulseTransactionCoordinator(); + + return loadAccessContext + .getSession() + .getPersistenceContextInternal() + .getNaturalIdResolutions() + .findCachedIdByNaturalId( normalizedNaturalIdValue, entityDescriptor ); + } + + private T findById(Object key, LoadAccessContext loadAccessContext) { + return withOptions( loadAccessContext, () -> { + final var session = loadAccessContext.getSession(); + Object result; + try { + result = loadAccessContext.load( + LoadEventListener.GET, + coerceId( key, session.getFactory() ), + entityDescriptor.getEntityName(), + makeLockOptions(), + readOnlyMode == ReadOnlyMode.READ_ONLY + ); + } + catch (ObjectNotFoundException notFoundException) { + // if session cache contains proxy for nonexisting object + result = null; + } + initializeIfNecessary( result ); + //noinspection unchecked + return (T) result; + } ); + } + + private LockOptions makeLockOptions() { + return Helper.makeLockOptions( lockMode, lockScope, lockTimeout, lockFollowOn ); + } + + // Used by Hibernate Reactive + protected Object coerceId(Object id, SessionFactoryImplementor factory) { + if ( factory.getSessionFactoryOptions().getJpaCompliance().isLoadByIdComplianceEnabled() ) { + return id; + } + + try { + return entityDescriptor.getIdentifierMapping().getJavaType().coerce( id ); + } + catch ( Exception e ) { + throw new IllegalArgumentException( "Argument '" + id + + "' could not be converted to the identifier type of entity '" + + entityDescriptor.getEntityName() + "'" + + " [" + e.getMessage() + "]", e ); + } + } + + private void initializeIfNecessary(Object result) { + if ( result != null ) { + final var lazyInitializer = extractLazyInitializer( result ); + if ( lazyInitializer != null ) { + if ( lazyInitializer.isUninitialized() ) { + lazyInitializer.initialize(); + } + } + else { + final var enhancementMetadata = entityDescriptor.getBytecodeEnhancementMetadata(); + if ( enhancementMetadata.isEnhancedForLazyLoading() + && enhancementMetadata.extractLazyInterceptor( result ) + instanceof EnhancementAsProxyLazinessInterceptor lazinessInterceptor ) { + lazinessInterceptor.forceInitialize( result, null ); + } + } + } + } + + @Override + public LockMode getLockMode() { + return lockMode; + } + + @Override + public Timeout getLockTimeout() { + return lockTimeout; + } + + @Override + public Locking.Scope getLockScope() { + return lockScope; + } + + @Override + public Locking.FollowOn getLockFollowOn() { + return lockFollowOn; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/internal/find/FindMultipleByKeyOperation.java b/hibernate-core/src/main/java/org/hibernate/internal/find/FindMultipleByKeyOperation.java new file mode 100644 index 000000000000..edf6d131a23a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/internal/find/FindMultipleByKeyOperation.java @@ -0,0 +1,346 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.internal.find; + +import jakarta.persistence.CacheRetrieveMode; +import jakarta.persistence.CacheStoreMode; +import jakarta.persistence.FindOption; +import jakarta.persistence.LockModeType; +import jakarta.persistence.PessimisticLockScope; +import jakarta.persistence.Timeout; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.BatchSize; +import org.hibernate.CacheMode; +import org.hibernate.EnabledFetchProfile; +import org.hibernate.KeyType; +import org.hibernate.LockMode; +import org.hibernate.LockOptions; +import org.hibernate.Locking; +import org.hibernate.NaturalIdSynchronization; +import org.hibernate.OrderingMode; +import org.hibernate.ReadOnlyMode; +import org.hibernate.RemovalsMode; +import org.hibernate.SessionCheckMode; +import org.hibernate.Timeouts; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.graph.GraphSemantic; +import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.jpa.internal.util.LockModeTypeHelper; +import org.hibernate.loader.ast.spi.MultiIdLoadOptions; +import org.hibernate.loader.ast.spi.MultiNaturalIdLoadOptions; +import org.hibernate.loader.internal.LoadAccessContext; +import org.hibernate.metamodel.mapping.NaturalIdMapping; +import org.hibernate.persister.entity.EntityPersister; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +import static org.hibernate.Timeouts.WAIT_FOREVER; +import static org.hibernate.internal.NaturalIdHelper.performAnyNeededCrossReferenceSynchronizations; +import static org.hibernate.jpa.SpecHints.HINT_SPEC_LOCK_TIMEOUT; + +/// Support for loading multiple entities (of a type) by key (either [id][KeyType#IDENTIFIER] or [natural-id][KeyType#NATURAL]). +/// +/// @see org.hibernate.Session#findMultiple +/// @see KeyType +/// +/// @author Steve Ebersole +public class FindMultipleByKeyOperation implements MultiIdLoadOptions, MultiNaturalIdLoadOptions { + private final EntityPersister entityDescriptor; + + private KeyType keyType = KeyType.IDENTIFIER; + + private BatchSize batchSize; + private SessionCheckMode sessionCheckMode = SessionCheckMode.DISABLED; + private RemovalsMode removalsMode = RemovalsMode.REPLACE; + private OrderingMode orderingMode = OrderingMode.ORDERED; + + private CacheStoreMode cacheStoreMode; + private CacheRetrieveMode cacheRetrieveMode; + + private LockMode lockMode; + private Locking.Scope lockScope; + private Locking.FollowOn lockFollowOn; + private Timeout lockTimeout = WAIT_FOREVER; + + private ReadOnlyMode readOnlyMode; + + private Set enabledFetchProfiles; + private Set disabledFetchProfiles; + + private NaturalIdSynchronization naturalIdSynchronization; + + @SuppressWarnings("PatternVariableHidesField") + public FindMultipleByKeyOperation( + @NonNull EntityPersister entityDescriptor, + @Nullable LockOptions defaultLockOptions, + @Nullable CacheMode defaultCacheMode, + boolean defaultReadOnly, + @NonNull SessionFactoryImplementor sessionFactory, + FindOption... findOptions) { + this.entityDescriptor = entityDescriptor; + + if ( defaultCacheMode != null ) { + cacheStoreMode = defaultCacheMode.getJpaStoreMode(); + cacheRetrieveMode = defaultCacheMode.getJpaRetrieveMode(); + } + + if ( defaultLockOptions != null ) { + lockMode = defaultLockOptions.getLockMode(); + lockScope = defaultLockOptions.getScope(); + lockTimeout = defaultLockOptions.getTimeout(); + lockFollowOn = defaultLockOptions.getFollowOnStrategy(); + } + if ( lockTimeout == WAIT_FOREVER ) { + final Object factoryTimeoutHint = sessionFactory.getProperties().get( HINT_SPEC_LOCK_TIMEOUT ); + if ( factoryTimeoutHint != null ) { + lockTimeout = Timeouts.fromHintTimeout( factoryTimeoutHint ); + } + } + + readOnlyMode = defaultReadOnly ? ReadOnlyMode.READ_ONLY : ReadOnlyMode.READ_WRITE; + + for ( FindOption option : findOptions ) { + if ( option instanceof KeyType keyType ) { + this.keyType = keyType; + } + else if ( option instanceof BatchSize batchSize ) { + this.batchSize = batchSize; + } + else if ( option instanceof SessionCheckMode sessionCheckMode ) { + this.sessionCheckMode = sessionCheckMode; + } + else if ( option instanceof RemovalsMode removalsMode ) { + this.removalsMode = removalsMode; + } + else if ( option instanceof OrderingMode orderingMode ) { + this.orderingMode = orderingMode; + } + else if ( option instanceof CacheStoreMode cacheStoreMode ) { + this.cacheStoreMode = cacheStoreMode; + } + else if ( option instanceof CacheRetrieveMode cacheRetrieveMode ) { + this.cacheRetrieveMode = cacheRetrieveMode; + } + else if ( option instanceof CacheMode cacheMode ) { + this.cacheStoreMode = cacheMode.getJpaStoreMode(); + this.cacheRetrieveMode = cacheMode.getJpaRetrieveMode(); + } + else if ( option instanceof LockModeType lockModeType ) { + this.lockMode = LockModeTypeHelper.getLockMode( lockModeType ); + } + else if ( option instanceof LockMode lockMode ) { + this.lockMode = lockMode; + } + else if ( option instanceof Locking.Scope lockScope ) { + this.lockScope = lockScope; + } + else if ( option instanceof PessimisticLockScope pessimisticLockScope ) { + this.lockScope = Locking.Scope.fromJpaScope( pessimisticLockScope ); + } + else if ( option instanceof Locking.FollowOn followOn ) { + this.lockFollowOn = followOn; + } + else if ( option instanceof Timeout timeout ) { + this.lockTimeout = timeout; + } + else if ( option instanceof ReadOnlyMode readOnlyMode) { + this.readOnlyMode = readOnlyMode; + } + else if ( option instanceof EnabledFetchProfile enabledFetchProfile ) { + this.enabledFetchProfile( enabledFetchProfile.profileName() ); + } + else if ( option instanceof NaturalIdSynchronization naturalIdSynchronization ) { + this.naturalIdSynchronization = naturalIdSynchronization; + } + } + } + + private void enabledFetchProfile(String profileName) { + if ( enabledFetchProfiles == null ) { + enabledFetchProfiles = new HashSet<>(); + } + enabledFetchProfiles.add( profileName ); + } + + public List performFind( + List keys, + @Nullable GraphSemantic graphSemantic, + @Nullable RootGraphImplementor rootGraph, + LoadAccessContext loadAccessContext) { + // todo (natural-id-class) : these impls are temporary + // longer term, move the logic here as much of it can be shared + if ( keyType == KeyType.NATURAL ) { + return findByNaturalIds( keys, graphSemantic, rootGraph, loadAccessContext ); + } + else { + return findByIds( keys, graphSemantic, rootGraph, loadAccessContext ); + } + } + + private List findByNaturalIds(List keys, GraphSemantic graphSemantic, RootGraphImplementor rootGraph, LoadAccessContext loadAccessContext) { + final NaturalIdMapping naturalIdMapping = entityDescriptor.requireNaturalIdMapping(); + final SessionImplementor session = loadAccessContext.getSession(); + + performAnyNeededCrossReferenceSynchronizations( + naturalIdSynchronization != NaturalIdSynchronization.DISABLED, + entityDescriptor, + session + ); + + return withOptions( loadAccessContext, graphSemantic, rootGraph, () -> { + // normalize the incoming natural-id values and get them in array form as needed + // by MultiNaturalIdLoader + final Object[] naturalIds = new Object[keys.size()]; + for ( int i = 0; i < keys.size(); i++ ) { + final Object key = keys.get( i ); + naturalIds[i] = naturalIdMapping.normalizeInput( key ); + } + + //noinspection unchecked + return (List)entityDescriptor.getMultiNaturalIdLoader() + .multiLoad( naturalIds, this, session ); + } ); + } + + private List withOptions( + LoadAccessContext loadAccessContext, + GraphSemantic graphSemantic, + RootGraphImplementor rootGraph, + Supplier> action) { + final var session = loadAccessContext.getSession(); + final var influencers = session.getLoadQueryInfluencers(); + final var fetchProfiles = influencers.adjustFetchProfiles( disabledFetchProfiles, enabledFetchProfiles ); + final var effectiveEntityGraph = rootGraph == null + ? null + : influencers.applyEntityGraph( rootGraph, graphSemantic ); + + final var readOnly = session.isDefaultReadOnly(); + session.setDefaultReadOnly( readOnlyMode == ReadOnlyMode.READ_ONLY ); + + final var cacheMode = session.getCacheMode(); + session.setCacheMode( CacheMode.fromJpaModes( cacheRetrieveMode, cacheStoreMode ) ); + + try { + return action.get(); + } + finally { + loadAccessContext.delayedAfterCompletion(); + if ( effectiveEntityGraph != null ) { + effectiveEntityGraph.clear(); + } + influencers.setEnabledFetchProfileNames( fetchProfiles ); + session.setDefaultReadOnly( readOnly ); + session.setCacheMode( cacheMode ); + } + } + + private Object getCachedNaturalIdResolution( + Object normalizedNaturalIdValue, + LoadAccessContext loadAccessContext) { + loadAccessContext.checkOpenOrWaitingForAutoClose(); + loadAccessContext.pulseTransactionCoordinator(); + + return loadAccessContext + .getSession() + .getPersistenceContextInternal() + .getNaturalIdResolutions() + .findCachedIdByNaturalId( normalizedNaturalIdValue, entityDescriptor ); + } + + private List findByIds(List keys, GraphSemantic graphSemantic, RootGraphImplementor rootGraph, LoadAccessContext loadAccessContext) { + final Object[] ids = keys.toArray( new Object[0] ); + //noinspection unchecked + return withOptions( loadAccessContext, graphSemantic, rootGraph, + () -> (List) entityDescriptor.multiLoad( ids, loadAccessContext.getSession(), this ) ); + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // MultiIdLoadOptions & MultiNaturalIdLoadOptions + + @Override + public SessionCheckMode getSessionCheckMode() { + return sessionCheckMode; + } + + @Override + public boolean isSecondLevelCacheCheckingEnabled() { + return cacheRetrieveMode == CacheRetrieveMode.USE; + } + + @Override + public Boolean getReadOnly(SessionImplementor session) { + return readOnlyMode == null ? null : readOnlyMode == ReadOnlyMode.READ_ONLY; + } + + @Override + public RemovalsMode getRemovalsMode() { + return removalsMode; + } + + @Override + public OrderingMode getOrderingMode() { + return orderingMode; + } + + @Override + public LockOptions getLockOptions() { + return Helper.makeLockOptions( lockMode, lockScope, lockTimeout, lockFollowOn ); + } + + @Override + public Integer getBatchSize() { + return batchSize == null ? null : batchSize.batchSize(); + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + /// Temporarily defined full constructor in support of + /// [org.hibernate.MultiIdentifierLoadAccess] and [org.hibernate.MultiIdentifierLoadAccess]. + /// + /// @deprecated [org.hibernate.MultiIdentifierLoadAccess] and [org.hibernate.MultiIdentifierLoadAccess] + /// are both also deprecated. + @Deprecated + public FindMultipleByKeyOperation( + EntityPersister entityDescriptor, + KeyType keyType, + BatchSize batchSize, + SessionCheckMode sessionCheckMode, + RemovalsMode removalsMode, + OrderingMode orderingMode, + CacheMode cacheMode, + LockOptions lockOptions, + ReadOnlyMode readOnlyMode, + Set enabledFetchProfiles, + Set disabledFetchProfiles, + NaturalIdSynchronization naturalIdSynchronization) { + if ( cacheMode == null ) { + cacheMode = CacheMode.NORMAL; + } + if ( lockOptions == null ) { + lockOptions = LockOptions.NONE; + } + this.entityDescriptor = entityDescriptor; + this.keyType = keyType; + this.batchSize = batchSize; + this.sessionCheckMode = sessionCheckMode; + this.removalsMode = removalsMode; + this.orderingMode = orderingMode; + this.cacheStoreMode = cacheMode.getJpaStoreMode(); + this.cacheRetrieveMode = cacheMode.getJpaRetrieveMode(); + this.lockMode = lockOptions.getLockMode(); + this.lockScope = lockOptions.getScope(); + this.lockFollowOn = lockOptions.getFollowOnStrategy(); + this.lockTimeout = lockOptions.getTimeout(); + this.readOnlyMode = readOnlyMode; + this.enabledFetchProfiles = enabledFetchProfiles; + this.disabledFetchProfiles = disabledFetchProfiles; + this.naturalIdSynchronization = naturalIdSynchronization; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/internal/find/Helper.java b/hibernate-core/src/main/java/org/hibernate/internal/find/Helper.java new file mode 100644 index 000000000000..7a43743efbf5 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/internal/find/Helper.java @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.internal.find; + +import jakarta.persistence.Timeout; +import org.hibernate.LockMode; +import org.hibernate.LockOptions; +import org.hibernate.Locking; +import org.hibernate.Timeouts; + +/** + * @author Steve Ebersole + */ +public class Helper { + public static LockOptions makeLockOptions(LockMode lockMode, Locking.Scope lockScope, Timeout lockTimeout, Locking.FollowOn lockFollowOn) { + if ( lockMode == null || lockMode == LockMode.NONE ) { + return LockOptions.NONE; + } + if ( lockMode == LockMode.READ ) { + return LockOptions.READ; + } + + final var lockOptions = new LockOptions( lockMode ); + lockOptions.setScope( lockScope != null ? lockScope : Locking.Scope.ROOT_ONLY ); + lockOptions.setTimeout( lockTimeout != null ? lockTimeout : Timeouts.WAIT_FOREVER ); + lockOptions.setFollowOnStrategy( lockFollowOn != null ? lockFollowOn : Locking.FollowOn.ALLOW ); + return lockOptions; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiNaturalIdLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiNaturalIdLoader.java index 8ac139d2d38d..513907e72b2c 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiNaturalIdLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiNaturalIdLoader.java @@ -144,9 +144,8 @@ private Object[] checkPersistenceContextForCachedResults( Consumer results ) { final List unresolvedIds = arrayList( naturalIds.length ); final var context = session.getPersistenceContextInternal(); - final var naturalIdMapping = getEntityDescriptor().getNaturalIdMapping(); for ( K naturalId : naturalIds ) { - final Object entity = entityForNaturalId( context, naturalIdMapping.normalizeInput( naturalId ) ); + final Object entity = entityForNaturalId( context, naturalId ); if ( entity != null ) { // Entity is already in the persistence context final var entry = context.getEntry( entity ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractNaturalIdLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractNaturalIdLoader.java index 600074f7b0e3..ce7b633e34cd 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractNaturalIdLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractNaturalIdLoader.java @@ -5,7 +5,10 @@ package org.hibernate.loader.ast.internal; import org.hibernate.HibernateException; +import org.hibernate.LockMode; import org.hibernate.LockOptions; +import org.hibernate.Locking; +import org.hibernate.Timeouts; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -32,7 +35,6 @@ import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.internal.BaseExecutionContext; import org.hibernate.sql.exec.internal.CallbackImpl; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.internal.SqlTypedMappingJdbcParameter; @@ -40,6 +42,7 @@ import org.hibernate.sql.exec.spi.JdbcParameterBinding; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.graph.internal.ImmutableFetchList; import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; @@ -86,13 +89,28 @@ public EntityMappingType getLoadable() { } @Override - public T load(Object naturalIdValue, NaturalIdLoadOptions options, SharedSessionContractImplementor session) { - final var factory = session.getFactory(); + public T load(Object naturalIdToLoad, Options options, SharedSessionContractImplementor session) { + final var lockOptions = makeLockOptions( options ); + return load( naturalIdToLoad, lockOptions, session ); + } + + private LockOptions makeLockOptions(Options options) { + if ( options.getLockMode() == null || options.getLockMode() == LockMode.NONE ) { + return LockOptions.NONE; + } + if ( options.getLockMode() == LockMode.READ ) { + return LockOptions.READ; + } + + final var lockOptions = new LockOptions( options.getLockMode() ); + lockOptions.setScope( options.getLockScope() != null ? options.getLockScope() : Locking.Scope.ROOT_ONLY ); + lockOptions.setTimeout( options.getLockTimeout() != null ? options.getLockTimeout() : Timeouts.WAIT_FOREVER ); + lockOptions.setFollowOnStrategy( options.getLockFollowOn() != null ? options.getLockFollowOn() : Locking.FollowOn.ALLOW ); + return lockOptions; + } - final var lockOptions = - options.getLockOptions() == null - ? new LockOptions() - : options.getLockOptions(); + private T load(Object naturalIdValue, LockOptions lockOptions, SharedSessionContractImplementor session) { + final var factory = session.getFactory(); final var sqlSelect = LoaderSelectBuilder.createSelect( getLoadable(), @@ -128,6 +146,14 @@ public T load(Object naturalIdValue, NaturalIdLoadOptions options, SharedSession ); } + @Override + public T load(Object naturalIdValue, NaturalIdLoadOptions options, SharedSessionContractImplementor session) { + final var lockOptions = options.getLockOptions() == null + ? new LockOptions() + : options.getLockOptions(); + return load( naturalIdValue, lockOptions, session ); + } + /** * Apply restriction necessary to match the given natural-id value. * Should also apply any predicates to the predicate consumer and @@ -148,9 +174,10 @@ protected Expression resolveColumnReference( TableGroup rootTableGroup, SelectableMapping selectableMapping, SqlExpressionResolver sqlExpressionResolver) { - final var tableReference = - rootTableGroup.getTableReference( rootTableGroup.getNavigablePath(), - selectableMapping.getContainingTableExpression() ); + final var tableReference = rootTableGroup.getTableReference( + rootTableGroup.getNavigablePath(), + selectableMapping.getContainingTableExpression() + ); if ( tableReference == null ) { throw new IllegalStateException( String.format( @@ -179,17 +206,16 @@ public Object resolveNaturalIdToId(Object naturalIdValue, SharedSessionContractI final var entityPath = new NavigablePath( entityDescriptor.getRootPathName() ); final var rootQuerySpec = new QuerySpec( true ); - final var sqlAstCreationState = - new LoaderSqlAstCreationState( - rootQuerySpec, - new SqlAliasBaseManager(), - new SimpleFromClauseAccessImpl(), - LockOptions.NONE, - (fetchParent, creationState) -> ImmutableFetchList.EMPTY, - true, - new LoadQueryInfluencers( factory ), - factory.getSqlTranslationEngine() - ); + final var sqlAstCreationState = new LoaderSqlAstCreationState( + rootQuerySpec, + new SqlAliasBaseManager(), + new SimpleFromClauseAccessImpl(), + LockOptions.NONE, + (fetchParent, creationState) -> ImmutableFetchList.EMPTY, + true, + new LoadQueryInfluencers( factory ), + factory.getSqlTranslationEngine() + ); final var rootTableGroup = entityDescriptor.createRootTableGroup( true, @@ -231,12 +257,13 @@ protected R executeNaturalIdQuery( Consumer predicateConsumer, LoaderSqlAstCreationState sqlAstCreationState, SharedSessionContractImplementor session) { + assert naturalIdMapping.isNormalized( naturalIdValue ); + final var factory = session.getFactory(); - final var bindings = - new JdbcParameterBindingsImpl( naturalIdMapping.getJdbcTypeCount() ); + final var bindings = new JdbcParameterBindingsImpl( naturalIdMapping.getJdbcTypeCount() ); applyNaturalIdRestriction( - naturalIdMapping().normalizeInput( naturalIdValue ), + naturalIdValue, rootTableGroup, predicateConsumer, bindings::addBinding, @@ -282,7 +309,7 @@ protected R executeNaturalIdQuery( }; } - private static JdbcOperationQuerySelect createJdbcOperationQuerySelect( + private static JdbcSelect createJdbcOperationQuerySelect( SelectStatement sqlSelect, SessionFactoryImplementor factory, JdbcParameterBindings bindings, diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java index 3045c41a60d5..0e4ad73fb5b7 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java @@ -25,9 +25,9 @@ import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.internal.SqlTypedMappingJdbcParameter; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.internal.RowTransformerStandardImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; @@ -48,7 +48,7 @@ public class CollectionBatchLoaderArrayParam private final SqlTypedMapping arraySqlTypedMapping; private final JdbcParameter jdbcParameter; private final SelectStatement sqlSelect; - private final JdbcOperationQuerySelect jdbcSelectOperation; + private final JdbcSelect jdbcSelectOperation; public CollectionBatchLoaderArrayParam( int domainBatchSize, diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderInPredicate.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderInPredicate.java index efea2c40a173..e8177fae30a3 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderInPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderInPredicate.java @@ -14,8 +14,8 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.query.spi.QueryOptions; import org.hibernate.sql.ast.tree.select.SelectStatement; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; import static org.hibernate.loader.ast.internal.MultiKeyLoadHelper.countIds; import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER; @@ -34,7 +34,7 @@ public class CollectionBatchLoaderInPredicate private final int sqlBatchSize; private final JdbcParametersList jdbcParameters; private final SelectStatement sqlAst; - private final JdbcOperationQuerySelect jdbcSelect; + private final JdbcSelect jdbcSelect; public CollectionBatchLoaderInPredicate( int domainBatchSize, diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSingleKey.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSingleKey.java index 4ff1caf44f74..38a1bf24dab0 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSingleKey.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSingleKey.java @@ -18,8 +18,8 @@ import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.internal.BaseExecutionContext; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.internal.RowTransformerStandardImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; @@ -36,7 +36,7 @@ public class CollectionLoaderSingleKey implements CollectionLoader { private final int keyJdbcCount; private final SelectStatement sqlAst; - private final JdbcOperationQuerySelect jdbcSelect; + private final JdbcSelect jdbcSelect; private final JdbcParametersList jdbcParameters; public CollectionLoaderSingleKey( diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/DatabaseSnapshotExecutor.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/DatabaseSnapshotExecutor.java index c676d11b812d..81ce23b7d327 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/DatabaseSnapshotExecutor.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/DatabaseSnapshotExecutor.java @@ -4,9 +4,6 @@ */ package org.hibernate.loader.ast.internal; -import java.util.ArrayList; -import java.util.List; - import org.hibernate.LockOptions; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -25,14 +22,17 @@ import org.hibernate.sql.exec.internal.BaseExecutionContext; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.internal.SqlTypedMappingJdbcParameter; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.internal.ImmutableFetchList; import org.hibernate.sql.results.internal.RowTransformerArrayImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; import org.hibernate.type.StandardBasicTypes; +import java.util.ArrayList; +import java.util.List; + import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_OBJECT_ARRAY; import static org.hibernate.loader.LoaderLogging.LOADER_LOGGER; import static org.hibernate.pretty.MessageHelper.infoString; @@ -44,7 +44,7 @@ class DatabaseSnapshotExecutor { private final EntityMappingType entityDescriptor; - private final JdbcOperationQuerySelect jdbcSelect; + private final JdbcSelect jdbcSelect; private final JdbcParametersList jdbcParameters; DatabaseSnapshotExecutor( diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java index 09322aa83d7d..bf009b055dc9 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java @@ -4,8 +4,6 @@ */ package org.hibernate.loader.ast.internal; -import java.util.Locale; - import org.hibernate.LockOptions; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -21,7 +19,9 @@ import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.internal.SqlTypedMappingJdbcParameter; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; +import org.hibernate.sql.exec.spi.JdbcSelect; + +import java.util.Locale; import static org.hibernate.loader.ast.internal.LoaderHelper.loadByArrayParameter; import static org.hibernate.loader.ast.internal.MultiKeyLoadHelper.trimIdBatch; @@ -44,7 +44,7 @@ public class EntityBatchLoaderArrayParam private final SqlTypedMapping arraySqlTypedMapping; private final JdbcParameter jdbcParameter; private final SelectStatement sqlAst; - private final JdbcOperationQuerySelect jdbcSelectOperation; + private final JdbcSelect jdbcSelectOperation; /** diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderInPredicate.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderInPredicate.java index 78dc80db9297..c594ad2d3510 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderInPredicate.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderInPredicate.java @@ -4,8 +4,6 @@ */ package org.hibernate.loader.ast.internal; -import java.util.Locale; - import org.hibernate.LockOptions; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -16,8 +14,10 @@ import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.query.spi.QueryOptions; import org.hibernate.sql.ast.tree.select.SelectStatement; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; + +import java.util.Locale; import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER; import static org.hibernate.pretty.MessageHelper.infoString; @@ -41,7 +41,7 @@ public class EntityBatchLoaderInPredicate private final JdbcParametersList jdbcParameters; private final SelectStatement sqlAst; - private final JdbcOperationQuerySelect jdbcSelectOperation; + private final JdbcSelect jdbcSelectOperation; /** * @param domainBatchSize The maximum number of entities we will initialize for each load diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java index b79661bbf050..192fd6b4766c 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java @@ -4,8 +4,6 @@ */ package org.hibernate.loader.ast.internal; -import java.util.List; - import org.hibernate.Hibernate; import org.hibernate.LockMode; import org.hibernate.LockOptions; @@ -26,12 +24,14 @@ import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.internal.RowTransformerStandardImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; import org.hibernate.type.descriptor.java.JavaType; +import java.util.List; + import static java.lang.System.arraycopy; import static java.lang.reflect.Array.newInstance; import static org.hibernate.loader.LoaderLogging.LOADER_LOGGER; @@ -217,7 +217,7 @@ public static X[] createTypedArray(Class elementClass, @SuppressWarnings( public static List loadByArrayParameter( K[] idsToInitialize, SelectStatement sqlAst, - JdbcOperationQuerySelect jdbcOperation, + JdbcSelect jdbcOperation, JdbcParameter jdbcParameter, JdbcMapping arrayJdbcMapping, Object entityId, diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadChunker.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadChunker.java index 18b8bbc55bf6..c450d5406e75 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadChunker.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadChunker.java @@ -10,9 +10,9 @@ import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.spi.ExecutionContext; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.internal.RowTransformerStandardImpl; import org.hibernate.sql.results.spi.ManagedResultConsumer; @@ -52,7 +52,7 @@ interface ChunkBoundaryListener { private final JdbcParametersList jdbcParameters; private final SelectStatement sqlAst; - private final JdbcOperationQuerySelect jdbcSelect; + private final JdbcSelect jdbcSelect; public MultiKeyLoadChunker( int chunkSize, @@ -60,7 +60,7 @@ public MultiKeyLoadChunker( Bindable bindable, JdbcParametersList jdbcParameters, SelectStatement sqlAst, - JdbcOperationQuerySelect jdbcSelect) { + JdbcSelect jdbcSelect) { this.chunkSize = chunkSize; this.keyColumnCount = keyColumnCount; this.bindable = bindable; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java index d9d723a73a18..730149aee2b9 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java @@ -4,9 +4,6 @@ */ package org.hibernate.loader.ast.internal; -import java.util.ArrayList; -import java.util.List; - import org.hibernate.LockOptions; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -17,12 +14,15 @@ import org.hibernate.query.spi.QueryOptionsAdapter; import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.internal.RowTransformerStandardImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; +import java.util.ArrayList; +import java.util.List; + import static org.hibernate.internal.util.collections.CollectionHelper.arrayList; /** @@ -49,7 +49,7 @@ interface KeyValueResolver { private final KeyValueResolver keyValueResolver; - private final JdbcOperationQuerySelect jdbcSelect; + private final JdbcSelect jdbcSelect; private final LockOptions lockOptions; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java index 166709179f7e..ccd7355e7ef7 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java @@ -4,8 +4,6 @@ */ package org.hibernate.loader.ast.internal; -import java.util.List; - import org.hibernate.HibernateException; import org.hibernate.LockOptions; import org.hibernate.engine.spi.LoadQueryInfluencers; @@ -26,12 +24,14 @@ import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.exec.spi.ExecutionContext; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; +import java.util.List; + import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; @@ -43,7 +43,7 @@ public class SingleUniqueKeyEntityLoaderStandard implements SingleUniqueKeyEn private final ModelPart uniqueKeyAttribute; private final String uniqueKeyAttributePath; private final JdbcParametersList jdbcParameters; - private final JdbcOperationQuerySelect jdbcSelect; + private final JdbcSelect jdbcSelect; public SingleUniqueKeyEntityLoaderStandard( EntityMappingType entityDescriptor, @@ -147,7 +147,7 @@ private JdbcParameterBindings jdbcParameterBindings( } private static List list( - JdbcOperationQuerySelect jdbcSelect, + JdbcSelect jdbcSelect, JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext) { return executionContext.getSession().getJdbcServices().getJdbcSelectExecutor() @@ -162,7 +162,7 @@ private static List list( ); } - private static JdbcOperationQuerySelect getJdbcSelect + private static JdbcSelect getJdbcSelect (SessionFactoryImplementor factory, SelectStatement sqlAst, JdbcParameterBindings jdbcParameterBindings) { return factory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory() .buildSelectTranslator( factory, sqlAst ) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiIdEntityLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiIdEntityLoader.java index 0063236946a9..48e5199b96a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiIdEntityLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiIdEntityLoader.java @@ -4,13 +4,15 @@ */ package org.hibernate.loader.ast.spi; -import java.util.List; - import org.hibernate.engine.spi.SharedSessionContractImplementor; -/** - * Loader subtype for loading multiple entities by multiple identifier values. - */ +import java.util.List; + +/// EntityMultiLoader implementation based on [identifier][org.hibernate.KeyType#IDENTIFIER]. +/// +/// @see org.hibernate.Session#findMultiple +/// +/// @author Steve Ebersole public interface MultiIdEntityLoader extends EntityMultiLoader { /** * Load multiple entities by id. The exact result depends on the passed options. diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiIdLoadOptions.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiIdLoadOptions.java index b1417d1ff3e5..eab7df01fe53 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiIdLoadOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiIdLoadOptions.java @@ -7,9 +7,14 @@ import org.hibernate.SessionCheckMode; import org.hibernate.engine.spi.SessionImplementor; -/** - * Encapsulation of the options for loading multiple entities by id - */ + +/// Encapsulation of the options for loading multiple entities (of a type) +/// by [id][org.hibernate.KeyType#IDENTIFIER]. +/// +/// @see org.hibernate.Session#findMultiple +/// @see MultiIdEntityLoader +/// +/// @author Steve Ebersole public interface MultiIdLoadOptions extends MultiLoadOptions { /** * Controls whether to check the current status of each identified entity diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiLoadOptions.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiLoadOptions.java index 2279ad8279c8..c84ad7039880 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiLoadOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiLoadOptions.java @@ -8,9 +8,13 @@ import org.hibernate.OrderingMode; import org.hibernate.RemovalsMode; -/** - * Base contract for options for multi-load operations - */ +/// Encapsulation of the options for loading multiple entities (of a type) +/// by [key][org.hibernate.KeyType]. +/// +/// @see MultiIdLoadOptions +/// @see MultiNaturalIdLoadOptions +/// +/// @author Steve Ebersole public interface MultiLoadOptions { /** * How should entities in removed status be handled. diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiNaturalIdLoadOptions.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiNaturalIdLoadOptions.java index 48ce0b5dc67b..f8d635636546 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiNaturalIdLoadOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiNaturalIdLoadOptions.java @@ -4,8 +4,11 @@ */ package org.hibernate.loader.ast.spi; -/** - * Encapsulation of the options for loading multiple entities by natural-id - */ +/// Encapsulation of the options for loading multiple entities (of a type) +/// by [natural-id][org.hibernate.KeyType#NATURAL]. +/// +/// @see MultiNaturalIdLoader +/// +/// @author Steve Ebersole public interface MultiNaturalIdLoadOptions extends MultiLoadOptions { } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiNaturalIdLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiNaturalIdLoader.java index 4aa96b303666..7f3c6bb4f8f3 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiNaturalIdLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/MultiNaturalIdLoader.java @@ -4,25 +4,25 @@ */ package org.hibernate.loader.ast.spi; -import java.util.List; - import org.hibernate.engine.spi.SharedSessionContractImplementor; -/** - * Loader for entities by multiple natural-ids - * - * @param The entity Java type - */ +import java.util.List; + +/// EntityMultiLoader implementation based on [identifier][org.hibernate.KeyType#NATURAL]. +/// +/// @param The entity Java type +/// +/// @see org.hibernate.Session#findMultiple +/// +/// @author Steve Ebersole public interface MultiNaturalIdLoader extends EntityMultiLoader { - /** - * Load multiple entities by natural-id. The exact result depends on the passed options. - * - * @param naturalIds The natural-ids to load. The values of this array will depend on whether the - * natural-id is simple or complex. - * - * @param The basic form for a natural-id is a Map of its attribute values, or an array of the - * values positioned according to "attribute ordering". Simple natural-ids can also be expressed - * by their simple (basic/embedded) type. - */ + /// Load multiple entities by natural-id. The exact result depends on the passed options. + /// + /// @param naturalIds The natural-ids to load. The values of this array will depend on whether the + /// natural-id is simple or complex. + /// + /// @param The basic form for a natural-id is a Map of its attribute values, or an array of the + /// values positioned according to "attribute ordering". Simple natural-ids can also be expressed + /// by their simple (basic/embedded) type. List multiLoad(K[] naturalIds, MultiNaturalIdLoadOptions options, SharedSessionContractImplementor session); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/NaturalIdLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/NaturalIdLoader.java index 9333140979de..388c4e52a0d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/NaturalIdLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/NaturalIdLoader.java @@ -4,30 +4,46 @@ */ package org.hibernate.loader.ast.spi; +import jakarta.persistence.Timeout; +import org.hibernate.LockMode; +import org.hibernate.Locking; import org.hibernate.engine.spi.SharedSessionContractImplementor; -/** - * Loader for {@link org.hibernate.annotations.NaturalId} handling - * - * @author Steve Ebersole - */ +/// Loader for [org.hibernate.annotations.NaturalId] +/// +/// @author Steve Ebersole public interface NaturalIdLoader extends EntityLoader, MultiKeyLoader { + interface Options { + LockMode getLockMode(); + Timeout getLockTimeout(); + Locking.Scope getLockScope(); + Locking.FollowOn getLockFollowOn(); + } - /** - * Perform the load of the entity by its natural-id - * - * @param naturalIdToLoad The natural-id to load. One of 2 forms accepted: - * * Single-value - valid for entities with a simple (single-valued) - * natural-id - * * Map - valid for any natural-id load. The map is each value keyed - * by the attribute name that the value corresponds to. Even though - * this form is allowed for simple natural-ids, the single value form - * should be used as it is more efficient - * @param options The options to apply to the load operation - * @param session The session into which the entity is being loaded - */ + /// Perform the load of the entity by its natural-id + /// + /// @param naturalIdToLoad The natural-id to load. One of 2 forms accepted: + /// * Single-value - valid for entities with a simple (single-valued) + /// natural-id + /// * Map - valid for any natural-id load. The map is each value keyed + /// by the attribute name that the value corresponds to. Even though + /// this form is allowed for simple natural-ids, the single value form + /// should be used as it is more efficient + /// @param options The options to apply to the load operation + /// @param session The session into which the entity is being loaded + /// + /// @deprecated (since 7.3) : use [#load(Object, Options, SharedSessionContractImplementor)] instead. + @Deprecated T load(Object naturalIdToLoad, NaturalIdLoadOptions options, SharedSessionContractImplementor session); + /// Perform the load of the entity by its natural-id + /// + /// @param naturalIdToLoad The [normalized][org.hibernate.metamodel.mapping.NaturalIdMapping#normalizeInput] + /// form of the natural-id. + /// @param options The options to apply to the load operation + /// @param session The session into which the entity is being loaded + T load(Object naturalIdToLoad, Options options, SharedSessionContractImplementor session); + /** * Resolve the id from natural-id value */ diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/BaseNaturalIdLoadAccessImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/BaseNaturalIdLoadAccessImpl.java index 14685b1c9ba7..1de0d68f623f 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/BaseNaturalIdLoadAccessImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/BaseNaturalIdLoadAccessImpl.java @@ -15,6 +15,7 @@ import org.hibernate.IdentifierLoadAccess; import org.hibernate.LockMode; import org.hibernate.LockOptions; +import org.hibernate.Locking; import org.hibernate.UnknownProfileException; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.Status; @@ -70,6 +71,14 @@ protected Object with(LockMode lockMode, PessimisticLockScope lockScope) { return this; } + protected Object with(Locking.Scope lockScope) { + if ( lockOptions == null ) { + lockOptions = new LockOptions(); + } + lockOptions.setScope( lockScope ); + return this; + } + protected Object with(Timeout timeout) { if ( lockOptions == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/SimpleNaturalIdLoadAccessImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/SimpleNaturalIdLoadAccessImpl.java index f041771aaa06..c083911ffed7 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/SimpleNaturalIdLoadAccessImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/SimpleNaturalIdLoadAccessImpl.java @@ -16,10 +16,12 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; +import org.hibernate.Locking; import org.hibernate.SimpleNaturalIdLoadAccess; import org.hibernate.graph.GraphSemantic; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.internal.SimpleNaturalIdMapping; +import org.hibernate.persister.entity.EntityPersister; /** * Implementation of {@link SimpleNaturalIdLoadAccess}. @@ -57,6 +59,11 @@ public SimpleNaturalIdLoadAccess with(LockMode lockMode, PessimisticLockScope return (SimpleNaturalIdLoadAccess) super.with( lockMode, lockScope ); } + public SimpleNaturalIdLoadAccess with(Locking.Scope lockScope) { + super.with( lockScope ); + return this; + } + @Override public SimpleNaturalIdLoadAccess with(Timeout timeout) { //noinspection unchecked @@ -99,7 +106,8 @@ private void verifySimplicity(Object naturalIdValue) { if ( !hasSimpleNaturalId && !naturalIdValue.getClass().isArray() && !(naturalIdValue instanceof List) - && !(naturalIdValue instanceof Map) ) { + && !(naturalIdValue instanceof Map) + && ! ( isNaturalIdClass( naturalIdValue ) ) ) { throw new HibernateException( String.format( Locale.ROOT, @@ -111,6 +119,11 @@ private void verifySimplicity(Object naturalIdValue) { } } + private boolean isNaturalIdClass(Object naturalIdValue) { + final EntityPersister entityPersister = entityPersister(); + return entityPersister.getNaturalIdMapping().getNaturalIdClass().isInstance( naturalIdValue ); + } + @Override public Optional loadOptional(Object naturalIdValue) { return Optional.ofNullable( load( naturalIdValue ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/RootClass.java b/hibernate-core/src/main/java/org/hibernate/mapping/RootClass.java index 6503fa477807..a4ec66d3a364 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/RootClass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/RootClass.java @@ -12,6 +12,7 @@ import org.hibernate.annotations.SoftDeleteType; import org.hibernate.boot.Metadata; import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.models.spi.ClassDetails; import static org.hibernate.internal.CoreMessageLogger.CORE_LOGGER; import static org.hibernate.internal.util.ReflectHelper.overridesEquals; @@ -34,6 +35,7 @@ public final class RootClass extends PersistentClass implements TableOwner, Soft private String cacheConcurrencyStrategy; private String cacheRegionName; private boolean lazyPropertiesCacheable = true; + private ClassDetails naturalIdClass; private String naturalIdCacheRegionName; private Value discriminator; @@ -365,6 +367,14 @@ public void setLazyPropertiesCacheable(boolean lazyPropertiesCacheable) { this.lazyPropertiesCacheable = lazyPropertiesCacheable; } + public ClassDetails getNaturalIdClass() { + return naturalIdClass; + } + + public void setNaturalIdClass(ClassDetails naturalIdClass) { + this.naturalIdClass = naturalIdClass; + } + @Override public String getNaturalIdCacheRegionName() { return naturalIdCacheRegionName; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java index 57f9b8762b89..6d39f18ec885 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java @@ -5,7 +5,10 @@ package org.hibernate.metamodel.mapping; import jakarta.persistence.Entity; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.Filter; +import org.hibernate.HibernateException; import org.hibernate.Incubating; import org.hibernate.Internal; import org.hibernate.annotations.ConcreteProxy; @@ -45,6 +48,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; +import static java.lang.String.format; import static org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer.UNFETCHED_PROPERTY; /** @@ -363,7 +367,16 @@ default OptimisticLockStyle optimisticLockStyle() { /** * The mapping for the natural-id of the entity, if one is defined */ - NaturalIdMapping getNaturalIdMapping(); + @Nullable NaturalIdMapping getNaturalIdMapping(); + + @NonNull + default NaturalIdMapping requireNaturalIdMapping() { + var mapping = getNaturalIdMapping(); + if ( mapping == null ) { + throw new HibernateException( format( "Entity %s does not specify a natural id", getEntityName() ) ); + } + return mapping; + } /** * The mapping for the row-id of the entity, if one is defined. diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/NaturalIdMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/NaturalIdMapping.java index 8ecf1ef79562..e066da830037 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/NaturalIdMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/NaturalIdMapping.java @@ -6,52 +6,49 @@ import java.util.List; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.Incubating; import org.hibernate.cache.spi.access.NaturalIdDataAccess; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.loader.ast.spi.MultiNaturalIdLoader; import org.hibernate.loader.ast.spi.NaturalIdLoader; -/** - * Mapping for an entity's natural-id, if one is defined. - *

- * Natural identifiers are an alternative form of uniquely - * identifying a specific row. In this sense, they are similar - * to a primary key. In fact most natural identifiers will also - * be classified as "candidate keys" (as in a column or group of - * columns that are considered candidates for primary-key). - *

- * However, a natural id has fewer restrictions than a primary - * key. While these lessened restrictions make them inappropriate - * for use as a primary key, they are still generally usable as - * unique locators with caveats. General reasons a natural id - * might be inappropriate for use as a primary key are

    - *
  • it contains nullable values
  • - *
  • it contains modifiable values
  • - *
- *

- * See other sources for a more complete discussion of data modeling. - * - * @see org.hibernate.Session#byNaturalId - * @see org.hibernate.Session#bySimpleNaturalId - * @see org.hibernate.Session#byMultipleNaturalId - * - * @author Steve Ebersole - */ +/// Mapping for an entity's natural-id, if one is defined. +/// +/// Natural identifiers are an alternative form of uniquely +/// identifying a specific row. In this sense, they are similar +/// to a primary key. In fact most natural identifiers will also +/// be classified as "candidate keys" (as in a column or group of +/// columns that are considered candidates for primary-key). +/// +/// However, a natural id has fewer restrictions than a primary +/// key. While these lessened restrictions make them inappropriate +/// for use as a primary key, they are still generally usable as +/// unique locators with caveats. General reasons a natural id +/// might be inappropriate for use as a primary key are +/// - it contains nullable values +/// - it contains modifiable values +/// +/// @see org.hibernate.annotations.NaturalId +/// @see org.hibernate.annotations.NaturalIdCache +/// @see org.hibernate.annotations.NaturalIdClass +/// @see org.hibernate.KeyType#NATURAL +/// +/// @author Steve Ebersole @Incubating public interface NaturalIdMapping extends VirtualModelPart { String PART_NAME = "{natural-id}"; - /** - * The attribute(s) making up the natural-id. - */ + /// A [class][org.hibernate.annotations.NaturalIdClass] which used + /// as a wrapper for natural-id values. + @Nullable Class getNaturalIdClass(); + + /// The attribute(s) making up the natural-id. List getNaturalIdAttributes(); - /** - * Whether the natural-id is mutable. - * - * @apiNote For compound natural-ids, this is true if any of the attributes are mutable. - */ + /// Whether the natural-id is mutable. + /// + /// @apiNote For compound natural-ids, this is true if any of the attributes are mutable. boolean isMutable(); @Override @@ -59,67 +56,54 @@ default String getPartName() { return PART_NAME; } - /** - * Access to the natural-id's L2 cache access. Returns null if the natural-id is not - * configured for caching - */ + /// Access to the natural-id's L2 cache access. + /// Returns null if the natural-id is not configured for caching. NaturalIdDataAccess getCacheAccess(); - /** - * Verify the natural-id value(s) we are about to flush to the database - */ + /// Verify the natural-id value(s) we are about to flush to the database void verifyFlushState( Object id, Object[] currentState, Object[] loadedState, SharedSessionContractImplementor session); - /** - * Given an array of "full entity state", extract the normalized natural id representation - * - * @param state The attribute state array - * - * @return The extracted natural id values. This is a normalized - */ + /// Given an array of "full entity state", extract the normalized natural id representation. + /// + /// @param state The attribute state array + /// + /// @return The extracted natural id values. Object extractNaturalIdFromEntityState(Object[] state); - /** - * Given an entity instance, extract the normalized natural id representation - * - * @param entity The entity instance - * - * @return The extracted natural id values - */ + /// Given an entity instance, extract the normalized natural-id representation. + /// + /// @param entity The entity instance + /// + /// @return The extracted natural-id values. Object extractNaturalIdFromEntity(Object entity); - /** - * Normalize a user-provided natural-id value into the representation Hibernate uses internally - * - * @param incoming The user-supplied value - * @return The normalized, internal representation - */ + /// Normalize a user-provided natural-id value into the representation Hibernate uses internally. + /// + /// @param incoming The user-supplied value + /// @return The normalized, internal representation Object normalizeInput(Object incoming); - /** - * Validates a natural id value(s) for the described natural-id based on the expected internal representation - */ + /// Whether the incoming value is in normalized internal form. + /// + /// @see #normalizeInput + boolean isNormalized(Object incoming); + + /// Validates a natural id value(s) for the described natural-id based on the expected internal representation void validateInternalForm(Object naturalIdValue); - /** - * Calculate the hash-code of a natural-id value - * - * @param value The natural-id value - * @return The hash-code - */ + /// Calculate the hash-code of a natural-id value + /// + /// @param value The natural-id value + /// @return The hash-code int calculateHashCode(Object value); - /** - * Make a loader capable of loading a single entity by natural-id - */ + /// Make a loader capable of loading a single entity by natural-id NaturalIdLoader makeLoader(EntityMappingType entityDescriptor); - /** - * Make a loader capable of loading multiple entities by natural-id - */ + /// Make a loader capable of loading multiple entities by natural-id MultiNaturalIdLoader makeMultiLoader(EntityMappingType entityDescriptor); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractNaturalIdMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractNaturalIdMapping.java index 52254432816e..bc048fff29c3 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractNaturalIdMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractNaturalIdMapping.java @@ -19,7 +19,9 @@ public abstract class AbstractNaturalIdMapping implements NaturalIdMapping { private final NavigableRole role; - public AbstractNaturalIdMapping(EntityMappingType declaringType, boolean mutable) { + public AbstractNaturalIdMapping( + EntityMappingType declaringType, + boolean mutable) { this.declaringType = declaringType; this.mutable = mutable; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CompoundNaturalIdMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CompoundNaturalIdMapping.java index 395328a6595e..0bcd550b280b 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CompoundNaturalIdMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CompoundNaturalIdMapping.java @@ -4,15 +4,12 @@ */ package org.hibernate.metamodel.mapping.internal; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; +import org.hibernate.MappingException; import org.hibernate.cache.MutableCacheKeyBuilder; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.IndexedConsumer; import org.hibernate.loader.ast.internal.CompoundNaturalIdLoader; @@ -20,6 +17,7 @@ import org.hibernate.loader.ast.spi.MultiNaturalIdLoader; import org.hibernate.loader.ast.spi.NaturalIdLoader; import org.hibernate.metamodel.UnsupportedMappingException; +import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.MappingType; @@ -27,6 +25,11 @@ import org.hibernate.metamodel.mapping.NaturalIdMapping; import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SingularAttributeMapping; +import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.ModelsContext; +import org.hibernate.property.access.spi.Getter; +import org.hibernate.property.access.spi.GetterFieldImpl; +import org.hibernate.property.access.spi.GetterMethodImpl; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.from.TableGroup; @@ -44,14 +47,25 @@ import org.hibernate.sql.results.jdbc.spi.RowProcessingState; import org.hibernate.type.descriptor.java.JavaType; +import java.beans.Introspector; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.RecordComponent; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + /** * Multi-attribute NaturalIdMapping implementation */ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implements MappingType, FetchableContainer { - // todo (6.0) : create a composite MappingType for this descriptor's Object[]? - private final List attributes; + private final ValueNormalizer valueNormalizer; private List jdbcMappings; /* @@ -60,11 +74,15 @@ This value is used to determine the size of the array used to create the Immutab */ private final int maxFetchableKeyIndex; + private final SessionFactoryImplementor sessionFactory; + public CompoundNaturalIdMapping( EntityMappingType declaringType, + ClassDetails naturalIdClass, List attributes, MappingModelCreationProcess creationProcess) { super( declaringType, isMutable( attributes ) ); + this.valueNormalizer = createValueNormalizer( naturalIdClass, attributes, creationProcess ); this.attributes = attributes; int maxIndex = 0; @@ -88,6 +106,8 @@ public CompoundNaturalIdMapping( return true; } ); + + this.sessionFactory = creationProcess.getCreationContext().getSessionFactory(); } private static boolean isMutable(List attributes) { @@ -99,6 +119,12 @@ private static boolean isMutable(List attributes) { return false; } + @Override + @Nullable + public Class getNaturalIdClass() { + return valueNormalizer.getIdClassType(); + } + @Override public Object[] extractNaturalIdFromEntityState(Object[] state) { if ( state == null ) { @@ -119,7 +145,7 @@ else if ( state.length == attributes.size() ) { @Override public Object[] extractNaturalIdFromEntity(Object entity) { - final var values = new Object[ attributes.size() ]; + final var values = new Object[attributes.size()]; for ( int i = 0; i < attributes.size(); i++ ) { values[i] = attributes.get( i ).getPropertyAccess().getGetter().get( entity ); } @@ -128,22 +154,22 @@ public Object[] extractNaturalIdFromEntity(Object entity) { @Override public Object[] normalizeInput(Object incoming) { + sessionFactory.getStatistics().normalizeNaturalId( getDeclaringType().getEntityName() ); + if ( incoming instanceof Object[] array ) { + // already normalized return array; } - else if ( incoming instanceof Map valueMap ) { - final var attributes = getNaturalIdAttributes(); - final var values = new Object[ attributes.size() ]; - for ( int i = 0; i < attributes.size(); i++ ) { - values[ i ] = valueMap.get( attributes.get( i ).getAttributeName() ); - } - return values; - } else { - throw new UnsupportedMappingException( "Could not normalize compound natural id value: " + incoming ); + return valueNormalizer.normalize( incoming ); } } + @Override + public boolean isNormalized(Object incoming) { + return incoming instanceof Object[]; + } + @Override public void validateInternalForm(Object naturalIdValue) { if ( naturalIdValue != null ) { @@ -343,7 +369,7 @@ else if ( domainValue instanceof Object[] values ) { } } else { - throw new AssertionFailure("Unexpected domain value type"); + throw new AssertionFailure( "Unexpected domain value type" ); } return span; } @@ -394,7 +420,7 @@ else if ( value instanceof Object[] incoming ) { return outgoing; } else { - throw new AssertionFailure("Unexpected value"); + throw new AssertionFailure( "Unexpected value" ); } } @@ -412,7 +438,7 @@ else if ( value instanceof Object[] values ) { } } else { - throw new AssertionFailure("Unexpected value"); + throw new AssertionFailure( "Unexpected value" ); } } @@ -453,7 +479,7 @@ else if ( value instanceof Object[] incoming ) { } } else { - throw new AssertionFailure("Unexpected value"); + throw new AssertionFailure( "Unexpected value" ); } return span; } @@ -481,7 +507,7 @@ else if ( value instanceof Object[] incoming ) { } } else { - throw new AssertionFailure("Unexpected value"); + throw new AssertionFailure( "Unexpected value" ); } return span; } @@ -509,7 +535,7 @@ public ModelPart findSubPart(String name, EntityMappingType treatTargetType) { @Override public void forEachSubPart(IndexedConsumer consumer, EntityMappingType treatTarget) { for ( int i = 0; i < attributes.size(); i++ ) { - consumer.accept( i, attributes.get(i) ); + consumer.accept( i, attributes.get( i ) ); } } @@ -630,9 +656,9 @@ private AssemblerImpl(ImmutableFetchList fetches, JavaType jtd, Assemb @Override public Object[] assemble(RowProcessingState rowProcessingState) { - final var result = new Object[ subAssemblers.length ]; + final var result = new Object[subAssemblers.length]; for ( int i = 0; i < subAssemblers.length; i++ ) { - result[ i ] = subAssemblers[i].assemble( rowProcessingState ); + result[i] = subAssemblers[i].assemble( rowProcessingState ); } return result; } @@ -662,4 +688,248 @@ public JavaType getAssembledJavaType() { } } + interface ValueNormalizer { + boolean isInstance(Object value); + Object[] normalize(Object value); + Class getIdClassType(); + } + + private static ValueNormalizer createValueNormalizer( + ClassDetails naturalIdClassDetails, + List keyAttributes, + MappingModelCreationProcess creationProcess) { + if ( naturalIdClassDetails == null ) { + return new ValueNormalizerSupport( keyAttributes ); + } + + final ModelsContext modelsContext = creationProcess + .getCreationContext() + .getBootstrapContext() + .getModelsContext(); + + var naturalIdClass = naturalIdClassDetails.toJavaClass( modelsContext.getClassLoading(), modelsContext ); + var naturalIdClassComponents = extractComponents( naturalIdClass ); + var naturalIdClassGetterAccess = createNaturalIdClassGetterAccess( naturalIdClass ); + + final List> attributeMappers = new ArrayList<>(); + keyAttributes.forEach( (keyAttribute) -> { + // find the matching MemberDetails on the `naturalIdClass`... + final Getter extractor = resolveMatchingExtractor( + naturalIdClass, + keyAttribute, + naturalIdClassGetterAccess, + naturalIdClassComponents, + modelsContext + ); + // todo (natural-id-class) : atm there is functionally no difference + // between BasicAttributeMapperImpl and ToOneAttributeMapperImpl. + // ideally we'd eventually support usage of the associated key entity's + // id and then there would. see the note in ToOneAttributeMapperImpl#extractFrom + final AttributeMapper attrMapper; + if ( keyAttribute instanceof ToOneAttributeMapping ) { + attrMapper = new ToOneAttributeMapperImpl<>( keyAttribute, extractor ); + } + else { + attrMapper = new BasicAttributeMapperImpl<>( keyAttribute, extractor ); + } + attributeMappers.add( attrMapper ); + } ); + + //noinspection unchecked,rawtypes + return new KeyClassNormalizer( keyAttributes, naturalIdClass, attributeMappers ); + } + + static class ValueNormalizerSupport implements ValueNormalizer { + private final List naturalIdAttributes; + + public ValueNormalizerSupport(List naturalIdAttributes) { + this.naturalIdAttributes = naturalIdAttributes; + } + + @Override + public boolean isInstance(Object value) { + return value instanceof Map; + } + + @Override + public Object[] normalize(Object incoming) { + if ( !isInstance( incoming ) ) { + throw new UnsupportedMappingException( "Could not normalize compound natural id value: " + incoming ); + } + final var values = new Object[naturalIdAttributes.size()]; + //noinspection unchecked + final Map valuesMap = (Map) incoming; + for ( int i = 0; i < naturalIdAttributes.size(); i++ ) { + values[i] = valuesMap.get( naturalIdAttributes.get( i ).getAttributeName() ); + } + return values; + } + + @Override + public Class getIdClassType() { + return null; + } + } + + /// Responsible for decomposing a value of the NaturalIdClass into the internal array format + static class KeyClassNormalizer extends ValueNormalizerSupport { + private final Class idClassType; + private final List> idClassAttributeMappers; + + public KeyClassNormalizer( + List naturalIdAttributes, + Class idClassType, + List> idClassAttributeMappers) { + super( naturalIdAttributes ); + this.idClassType = idClassType; + this.idClassAttributeMappers = idClassAttributeMappers; + } + + @Override + public Class getIdClassType() { + return idClassType; + } + + @Override + public Object[] normalize(Object value) { + if ( idClassType.isInstance( value ) ) { + return doNormalize( idClassType.cast( value ) ); + } + + return super.normalize( value ); + } + + public Object[] doNormalize(T idClassValue) { + final Object[] result = new Object[idClassAttributeMappers.size()]; + for ( int i = 0; i < idClassAttributeMappers.size(); i++ ) { + var value = idClassAttributeMappers.get( i ).extractFrom( idClassValue ); + result[i] = value; + } + return result; + } + + public boolean isInstance(Object value) { + return idClassType.isInstance( value ) || super.isInstance( value ); + } + } + + private static Function createNaturalIdClassGetterAccess(Class naturalIdClass) { + return new Function<>() { + private Map getterMethods; + @Override + public Method apply(String name) { + if ( getterMethods == null ) { + getterMethods = extractGetterMethods( naturalIdClass ); + } + return getterMethods.get( name ); + } + }; + } + + private static Getter resolveMatchingExtractor( + Class naturalIdClass, + AttributeMapping keyAttribute, + Function getterMethodAccess, + Map naturalIdClassComponents, + ModelsContext modelsContext) { + // first, if the `naturalIdClass` is a record, look for a component + if ( naturalIdClass.isRecord() ) { + var component = naturalIdClassComponents.get( keyAttribute.getAttributeName() ); + if ( component != null ) { + return new GetterMethodImpl( + naturalIdClass, + keyAttribute.getAttributeName(), + component.getAccessor() + ); + } + } + + // next look for a getter method + var getterMethod = getterMethodAccess.apply( keyAttribute.getAttributeName() ); + if ( getterMethod != null ) { + return new GetterMethodImpl( + naturalIdClass, + keyAttribute.getAttributeName(), + getterMethod + ); + } + + // lastly, look for a field + try { + var field = naturalIdClass.getDeclaredField( keyAttribute.getAttributeName() ); + return new GetterFieldImpl( naturalIdClass, keyAttribute.getAttributeName(), field ); + } + catch (NoSuchFieldException ignore) { + } + + throw new MappingException( "Unable to find NaturalIdClass accessor for natural-id attribute: " + keyAttribute.getAttributeName() ); + } + + private static Map extractGetterMethods(Class naturalIdClass) { + final Map result = new HashMap<>(); + + for ( Method declaredMethod : naturalIdClass.getDeclaredMethods() ) { + if ( declaredMethod.getParameterCount() == 0 + && declaredMethod.getReturnType() != void.class + && !Modifier.isStatic( declaredMethod.getModifiers() ) ) { + var methodName = declaredMethod.getName(); + if ( methodName.startsWith( "is" ) ) { + result.put( + Introspector.decapitalize( methodName.substring( 2 ) ), + declaredMethod + ); + } + else if ( methodName.startsWith( "get" ) ) { + result.put( + Introspector.decapitalize( methodName.substring( 3 ) ), + declaredMethod + ); + } + } + } + + return result; + } + + private static Map extractComponents(Class naturalIdClass) { + if ( !naturalIdClass.isRecord() ) { + return Map.of(); + } + + final RecordComponent[] recordComponents = naturalIdClass.getRecordComponents(); + final Map result = new HashMap<>(); + for ( RecordComponent recordComponent : recordComponents ) { + result.put( recordComponent.getName(), recordComponent ); + } + return result; + } + + public interface AttributeMapper { + V extractFrom(T keyValue); + } + + /// AttributeMapper for both basic and embedded values + public record BasicAttributeMapperImpl(AttributeMapping entityAttribute, Getter keyClassExtractor) + implements AttributeMapper { + @Override + public Object extractFrom(T keyValue) { + return keyClassExtractor.get( keyValue ); + } + } + + /// AttributeMapper for to-one values + public record ToOneAttributeMapperImpl(AttributeMapping entityAttribute, Getter keyClassExtractor) + implements AttributeMapper { + @Override + public Object extractFrom(T keyValue) { + // todo (natural-id-class) : handle "key -> to-one" resolutions + // this requires some contract changes though to pass Session + // to be able to resolve key -> entity for the to-one. + // + + /// the other difficulty is handling "derived id" structures + // + // see `NaturalIdMapping#normalizeInput` + return keyClassExtractor.get( keyValue ); + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java index 9b8473a974d8..7c6b183476f0 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java @@ -4,9 +4,6 @@ */ package org.hibernate.metamodel.mapping.internal; -import java.util.ArrayList; -import java.util.List; - import org.hibernate.Incubating; import org.hibernate.LockMode; import org.hibernate.LockOptions; @@ -25,11 +22,14 @@ import org.hibernate.query.spi.QueryOptions; import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.internal.RowTransformerArrayImpl; +import java.util.ArrayList; +import java.util.List; + import static org.hibernate.sql.results.spi.ListResultsConsumer.UniqueSemantic.FILTER; /** @@ -51,7 +51,7 @@ @Incubating public class GeneratedValuesProcessor { private final SelectStatement selectStatement; - private final JdbcOperationQuerySelect jdbcSelect; + private final JdbcSelect jdbcSelect; private final List generatedValuesToSelect; private final JdbcParametersList jdbcParameters; @@ -217,7 +217,7 @@ public EntityMappingType getEntityDescriptor() { return entityDescriptor; } - public JdbcOperationQuerySelect getJdbcSelect() { + public JdbcSelect getJdbcSelect() { return jdbcSelect; } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleNaturalIdMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleNaturalIdMapping.java index f647a84a9aa4..b53d6688ea4f 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleNaturalIdMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleNaturalIdMapping.java @@ -10,6 +10,7 @@ import java.util.Map; import java.util.function.BiConsumer; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.HibernateException; import org.hibernate.cache.MutableCacheKeyBuilder; import org.hibernate.dialect.Dialect; @@ -103,12 +104,17 @@ public Object extractNaturalIdFromEntity(Object entity) { return attribute.getPropertyAccess().getGetter().get( entity ); } + @Override + public boolean isNormalized(Object incoming) { + return incoming == null || getJavaType().getJavaTypeClass().isInstance( incoming ); + } + @Override public void validateInternalForm(Object naturalIdValue) { if ( naturalIdValue != null ) { final var naturalIdValueClass = naturalIdValue.getClass(); + // be flexible - allow a single-valued array if ( naturalIdValueClass.isArray() && !naturalIdValueClass.getComponentType().isPrimitive() ) { - // be flexible final var values = (Object[]) naturalIdValue; if ( values.length == 1 ) { naturalIdValue = values[0]; @@ -144,6 +150,8 @@ public Object normalizeInput(Object incoming) { } private Object normalizedValue(Object incoming) { + sessionFactory.getStatistics().normalizeNaturalId( getDeclaringType().getEntityName() ); + if ( incoming instanceof Map valueMap ) { assert valueMap.size() == 1; assert valueMap.containsKey( getAttribute().getAttributeName() ); @@ -167,6 +175,12 @@ public List getNaturalIdAttributes() { return Collections.singletonList( attribute ); } + @Override + @Nullable + public Class getNaturalIdClass() { + return null; + } + @Override public MappingType getPartMappingType() { return attribute.getPartMappingType(); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index d64878d30bde..a57d1951fdfd 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -4844,8 +4844,9 @@ else if ( bootEntityDescriptor.hasNaturalId() ) { } } - protected NaturalIdMapping generateNaturalIdMapping - (MappingModelCreationProcess creationProcess, PersistentClass bootEntityDescriptor) { + protected NaturalIdMapping generateNaturalIdMapping( + MappingModelCreationProcess creationProcess, + PersistentClass bootEntityDescriptor) { //noinspection AssertWithSideEffects assert bootEntityDescriptor.hasNaturalId(); @@ -4853,9 +4854,16 @@ else if ( bootEntityDescriptor.hasNaturalId() ) { assert naturalIdAttributeIndexes.length > 0; if ( naturalIdAttributeIndexes.length == 1 ) { + if ( bootEntityDescriptor.getRootClass().getNaturalIdClass() != null ) { + throw new UnsupportedMappingException( "NaturalIdClass not supported for simple naturaal-id mappings" ); + } final String propertyName = getPropertyNames()[ naturalIdAttributeIndexes[ 0 ] ]; final var attributeMapping = (SingularAttributeMapping) findAttributeMapping( propertyName ); - return new SimpleNaturalIdMapping( attributeMapping, this, creationProcess ); + return new SimpleNaturalIdMapping( + attributeMapping, + this, + creationProcess + ); } // collect the names of the attributes making up the natural-id. @@ -4879,7 +4887,12 @@ else if ( bootEntityDescriptor.hasNaturalId() ) { throw new MappingException( "Expected multiple natural-id attributes, but found only one: " + getEntityName() ); } - return new CompoundNaturalIdMapping(this, collectedAttrMappings, creationProcess ); + return new CompoundNaturalIdMapping( + this, + bootEntityDescriptor.getRootClass().getNaturalIdClass(), + collectedAttrMappings, + creationProcess + ); } protected static SqmMultiTableMutationStrategy interpretSqmMultiTableStrategy( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java index 9d66ce48869e..1d683398789d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java @@ -4,10 +4,6 @@ */ package org.hibernate.query.sqm.mutation.internal; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; @@ -44,18 +40,21 @@ import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SelectStatement; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.basic.BasicResult; import org.hibernate.sql.results.internal.RowTransformerArrayImpl; import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; import org.hibernate.sql.results.spi.RowTransformer; - import org.jboss.logging.Logger; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + /** * Helper used to generate the SELECT for selection of an entity's identifier, here specifically intended to be used * as the SELECT portion of a multi-table SQM mutation @@ -162,7 +161,7 @@ public static SqmSelectStatement generateMatchingIdSelectStatement( * Centralized selection of ids matching the restriction of the DELETE * or UPDATE SQM query */ - public static CacheableSqmInterpretation createMatchingIdsSelect( + public static CacheableSqmInterpretation createMatchingIdsSelect( SqmDeleteOrUpdateStatement sqmMutationStatement, DomainParameterXref domainParameterXref, DomainQueryExecutionContext executionContext, @@ -215,7 +214,7 @@ public static CacheableSqmInterpretation translation = translator.translate(); final JdbcServices jdbcServices = factory.getJdbcServices(); final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); - final SqlAstTranslator sqlAstSelectTranslator = jdbcEnvironment + final SqlAstTranslator sqlAstSelectTranslator = jdbcEnvironment .getSqlAstTranslatorFactory() .buildSelectTranslator( factory, translation.getSqlAst() ); @@ -267,13 +266,13 @@ public static List selectMatchingIds( DomainParameterXref domainParameterXref, DomainQueryExecutionContext executionContext) { final MutableObject jdbcParameterBindings = new MutableObject<>(); - final CacheableSqmInterpretation interpretation = + final CacheableSqmInterpretation interpretation = createMatchingIdsSelect( sqmMutationStatement, domainParameterXref, executionContext, jdbcParameterBindings ); return selectMatchingIds( interpretation, jdbcParameterBindings.get(), executionContext ); } public static List selectMatchingIds( - CacheableSqmInterpretation interpretation, + CacheableSqmInterpretation interpretation, JdbcParameterBindings jdbcParameterBindings, DomainQueryExecutionContext executionContext) { final RowTransformer rowTransformer; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java index d36d4c9b1892..59b3f969b731 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java @@ -4,12 +4,6 @@ */ package org.hibernate.query.sqm.mutation.internal.cte; -import java.util.ArrayList; -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; - import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.engine.jdbc.spi.JdbcServices; @@ -26,9 +20,9 @@ import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.query.sqm.internal.SqmUtil; +import org.hibernate.query.sqm.mutation.internal.AbstractMutationHandler; import org.hibernate.query.sqm.mutation.internal.MatchingIdSelectionHelper; import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; -import org.hibernate.query.sqm.mutation.internal.AbstractMutationHandler; import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; import org.hibernate.query.sqm.tree.expression.SqmExpression; @@ -54,15 +48,21 @@ import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SelectClause; import org.hibernate.sql.ast.tree.select.SelectStatement; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.basic.BasicResult; import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl; import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; + /** * Defines how identifier values are selected from the updatable/deletable tables. * @@ -76,7 +76,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler private final Map, Map, List>> jdbcParamsXref; private final Map, MappingModelExpressible> resolvedParameterMappingModelTypes; - private final JdbcOperationQuerySelect select; + private final JdbcSelect select; public AbstractCteMutationHandler( CteTable cteTable, @@ -140,7 +140,7 @@ public AbstractCteMutationHandler( final List> domainResults = new ArrayList<>( 1 ); final SelectStatement statement = new SelectStatement( querySpec, domainResults ); final JdbcServices jdbcServices = factory.getJdbcServices(); - final SqlAstTranslator translator = jdbcServices.getJdbcEnvironment() + final SqlAstTranslator translator = jdbcServices.getJdbcEnvironment() .getSqlAstTranslatorFactory() .buildSelectTranslator( factory, statement ); @@ -232,7 +232,7 @@ public int execute(JdbcParameterBindings jdbcParameterBindings, DomainQueryExecu } // For Hibernate Reactive - protected JdbcOperationQuerySelect getSelect() { + protected JdbcSelect getSelect() { return select; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java index ffe734e4e842..e34122067812 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java @@ -91,9 +91,9 @@ import org.hibernate.sql.ast.tree.select.SortSpecification; import org.hibernate.sql.ast.tree.update.Assignment; import org.hibernate.sql.ast.tree.update.UpdateStatement; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.basic.BasicResult; import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl; @@ -124,7 +124,7 @@ public class CteInsertHandler implements InsertHandler { private final Map, Map, List>> jdbcParamsXref; private final Map, MappingModelExpressible> resolvedParameterMappingModelTypes; - private final JdbcOperationQuerySelect select; + private final JdbcSelect select; public CteInsertHandler( CteTable cteTable, @@ -552,7 +552,7 @@ else if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) // Execute the statement final JdbcServices jdbcServices = factory.getJdbcServices(); - final SqlAstTranslator translator = jdbcServices.getJdbcEnvironment() + final SqlAstTranslator translator = jdbcServices.getJdbcEnvironment() .getSqlAstTranslatorFactory() .buildSelectTranslator( factory, statement ); @@ -622,7 +622,7 @@ public int execute(JdbcParameterBindings jdbcParameterBindings, DomainQueryExecu } // For Hibernate Reactive - protected JdbcOperationQuerySelect getSelect() { + protected JdbcSelect getSelect() { return select; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/AbstractInlineHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/AbstractInlineHandler.java index f31e0028fb97..8f532d58c44e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/AbstractInlineHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/AbstractInlineHandler.java @@ -20,8 +20,8 @@ import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.sql.ast.tree.select.SelectStatement; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; +import org.hibernate.sql.exec.spi.JdbcSelect; /** @@ -34,7 +34,7 @@ public abstract class AbstractInlineHandler implements Handler { private final MatchingIdRestrictionProducer matchingIdsPredicateProducer; private final EntityPersister entityDescriptor; private final DomainParameterXref domainParameterXref; - private final CacheableSqmInterpretation matchingIdsInterpretation; + private final CacheableSqmInterpretation matchingIdsInterpretation; protected AbstractInlineHandler( MatchingIdRestrictionProducer matchingIdsPredicateProducer, @@ -95,7 +95,7 @@ protected DomainParameterXref getDomainParameterXref() { return domainParameterXref; } - protected CacheableSqmInterpretation getMatchingIdsInterpretation() { + protected CacheableSqmInterpretation getMatchingIdsInterpretation() { return matchingIdsInterpretation; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java index aa39b4c288c7..4963dbb7be21 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java @@ -4,18 +4,6 @@ */ package org.hibernate.query.sqm.mutation.internal.temptable; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.function.BiConsumer; -import java.util.function.Function; -import java.util.stream.IntStream; - import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableColumn; @@ -95,18 +83,29 @@ import org.hibernate.sql.exec.internal.SqlTypedMappingJdbcParameter; import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBinder; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.graph.basic.BasicFetch; import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; import org.hibernate.type.BasicType; - import org.hibernate.type.descriptor.ValueBinder; import org.jboss.logging.Logger; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.stream.IntStream; + import static org.hibernate.generator.EventType.INSERT; import static org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper.isId; @@ -476,7 +475,7 @@ private void collectTableReference( } protected record RootTableInserter( - @Nullable JdbcOperationQuerySelect temporaryTableIdentitySelect, + @Nullable JdbcSelect temporaryTableIdentitySelect, @Nullable JdbcOperationQueryMutation temporaryTableIdUpdate, @Nullable String temporaryTableRowNumberSelectSql, JdbcOperationQueryMutation rootTableInsert, @@ -541,7 +540,7 @@ private RootTableInserter createRootTableInserter( applyAssignments( assignments, insertStatement, temporaryTableReference, getEntityDescriptor() ); final JdbcServices jdbcServices = getSessionFactory().getJdbcServices(); final SharedSessionContractImplementor session = executionContext.getSession(); - final JdbcOperationQuerySelect temporaryTableIdentitySelect; + final JdbcSelect temporaryTableIdentitySelect; final JdbcOperationQueryMutation temporaryTableIdUpdate; final String temporaryTableRowNumberSelectSql; final JdbcOperationQueryMutation rootTableInsert; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslatorFactory.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslatorFactory.java index 8727488b9187..12604f6f74ec 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslatorFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslatorFactory.java @@ -8,7 +8,7 @@ import org.hibernate.sql.ast.tree.MutationStatement; import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.model.ast.TableMutation; import org.hibernate.sql.model.jdbc.JdbcMutationOperation; @@ -21,7 +21,7 @@ public interface SqlAstTranslatorFactory { /** * Builds a single-use select translator */ - SqlAstTranslator buildSelectTranslator(SessionFactoryImplementor sessionFactory, SelectStatement statement); + SqlAstTranslator buildSelectTranslator(SessionFactoryImplementor sessionFactory, SelectStatement statement); /** * Builds a single-use mutation translator diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslatorFactory.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslatorFactory.java index 59cf7cc5c62d..a72adb7b93a3 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslatorFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslatorFactory.java @@ -12,7 +12,7 @@ import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; +import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.model.ast.TableMutation; import org.hibernate.sql.model.jdbc.JdbcMutationOperation; @@ -24,7 +24,7 @@ public class StandardSqlAstTranslatorFactory implements SqlAstTranslatorFactory { @Override - public SqlAstTranslator buildSelectTranslator(SessionFactoryImplementor sessionFactory, SelectStatement statement) { + public SqlAstTranslator buildSelectTranslator(SessionFactoryImplementor sessionFactory, SelectStatement statement) { return buildTranslator( sessionFactory, statement ); } diff --git a/hibernate-core/src/main/java/org/hibernate/stat/NaturalIdStatistics.java b/hibernate-core/src/main/java/org/hibernate/stat/NaturalIdStatistics.java index 3604c5d4e1e6..655f8d5fac71 100644 --- a/hibernate-core/src/main/java/org/hibernate/stat/NaturalIdStatistics.java +++ b/hibernate-core/src/main/java/org/hibernate/stat/NaturalIdStatistics.java @@ -48,4 +48,11 @@ public interface NaturalIdStatistics extends CacheableDataStatistics, Serializab * the execution of this "natural id resolution" query */ long getExecutionMinTime(); + + /** + * The number of times (since last Statistics clearing) that natural-id value + * {@linkplain org.hibernate.metamodel.mapping.NaturalIdMapping#normalizeInput normalization} + * has been performed. + */ + long getNormalizationCount(); } diff --git a/hibernate-core/src/main/java/org/hibernate/stat/internal/NaturalIdStatisticsImpl.java b/hibernate-core/src/main/java/org/hibernate/stat/internal/NaturalIdStatisticsImpl.java index 843c2013f995..284efeb6cec9 100644 --- a/hibernate-core/src/main/java/org/hibernate/stat/internal/NaturalIdStatisticsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/stat/internal/NaturalIdStatisticsImpl.java @@ -24,6 +24,7 @@ public class NaturalIdStatisticsImpl extends AbstractCacheableDataStatistics imp private final AtomicLong executionMaxTime = new AtomicLong(); private final AtomicLong executionMinTime = new AtomicLong( Long.MAX_VALUE ); private final AtomicLong totalExecutionTime = new AtomicLong(); + private final AtomicLong normalizationCount = new AtomicLong(); private final Lock readLock; private final Lock writeLock; @@ -81,6 +82,11 @@ public long getExecutionMinTime() { return executionMinTime.get(); } + @Override + public long getNormalizationCount() { + return normalizationCount.get(); + } + void queryExecuted(long time) { // read lock is enough, concurrent updates are supported by the underlying type AtomicLong // this only guards executed(long, long) to be called, when another thread is executing getExecutionAvgTime() @@ -105,6 +111,10 @@ void queryExecuted(long time) { } } + void valueNormalized() { + normalizationCount.getAndIncrement(); + } + @Override public String toString() { final var text = new StringBuilder() diff --git a/hibernate-core/src/main/java/org/hibernate/stat/internal/StatisticsImpl.java b/hibernate-core/src/main/java/org/hibernate/stat/internal/StatisticsImpl.java index ac256ff03224..249ee879e51a 100644 --- a/hibernate-core/src/main/java/org/hibernate/stat/internal/StatisticsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/stat/internal/StatisticsImpl.java @@ -1044,4 +1044,9 @@ public Map getSlowQueries() { public void slowQuery(String sql, long executionTime) { slowQueries.merge( sql, executionTime, Math::max ); } + + @Override + public void normalizeNaturalId(String entityName) { + getNaturalIdStatistics( entityName ).valueNormalized(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/stat/spi/StatisticsImplementor.java b/hibernate-core/src/main/java/org/hibernate/stat/spi/StatisticsImplementor.java index 28ec8450b033..6dac7eb0a69f 100644 --- a/hibernate-core/src/main/java/org/hibernate/stat/spi/StatisticsImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/stat/spi/StatisticsImplementor.java @@ -305,4 +305,8 @@ default Map getSlowQueries() { //For backward compatibility return emptyMap(); } + + default void normalizeNaturalId(String entityName) { + //For backward compatibility + } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/A.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/A.java deleted file mode 100644 index 7ef314b20d22..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/A.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.annotations.naturalid; - -import java.util.HashSet; -import java.util.Set; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Version; - -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; -import org.hibernate.annotations.NaturalId; -import org.hibernate.annotations.NaturalIdCache; - -/** - * @author Guenther Demetz - */ -@Entity -@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) -@NaturalIdCache -public class A { - - @Id - @GeneratedValue(strategy = GenerationType.TABLE) - private long oid; - - @Version - private int version; - - @Column - @NaturalId(mutable = false) - private String name; - - @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) - @org.hibernate.annotations.OptimisticLock(excluded = true) - @jakarta.persistence.OneToMany(mappedBy = "a") - private Set ds = new HashSet(); - - @jakarta.persistence.OneToOne - private D singleD = null; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Set getDs() { - return ds; - } - - public void setDs(Set ds) { - this.ds = ds; - } - - public D getSingleD() { - return singleD; - } - - public void setSingleD(D singleD) { - this.singleD = singleD; - } - -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/Citizen.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/Citizen.java deleted file mode 100644 index 1254fac4e0ce..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/Citizen.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.annotations.naturalid; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToOne; - -import org.hibernate.annotations.NaturalId; -import org.hibernate.annotations.NaturalIdCache; - -/** - * @author Emmanuel Bernard - */ -@Entity -@NaturalIdCache -public class Citizen { - @Id - private Integer id; - private String firstname; - private String lastname; - @NaturalId - @ManyToOne - private State state; - @NaturalId - private String ssn; - - public Citizen() { - } - - public Citizen(Integer id, String firstname, String lastname, State state, String ssn) { - this.id = id; - this.firstname = firstname; - this.lastname = lastname; - this.state = state; - this.ssn = ssn; - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getFirstname() { - return firstname; - } - - public void setFirstname(String firstname) { - this.firstname = firstname; - } - - public String getLastname() { - return lastname; - } - - public void setLastname(String lastname) { - this.lastname = lastname; - } - - public State getState() { - return state; - } - - public void setState(State state) { - this.state = state; - } - - public String getSsn() { - return ssn; - } - - public void setSsn(String ssn) { - this.ssn = ssn; - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/D.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/D.java deleted file mode 100644 index 49c2e1ddf8f3..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/D.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.annotations.naturalid; - -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Version; - -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; - -/** - * @author Guenther Demetz - */ -@Entity -@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) -public class D { - @Id - @GeneratedValue(strategy = GenerationType.TABLE) - public long oid; - - @Version - private int version; - - @jakarta.persistence.ManyToOne(fetch = FetchType.LAZY) - private A a = null; - - @jakarta.persistence.OneToOne(mappedBy = "singleD") - private A singleA = null; - - public A getA() { - return a; - } - - public void setA(A a) { - this.a = a; - } - - public A getSingleA() { - return singleA; - } - - public void setSingleA(A singleA) { - this.singleA = singleA; - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdOnManyToOne.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdOnManyToOne.java deleted file mode 100644 index 53730932a71d..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdOnManyToOne.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.annotations.naturalid; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToOne; - -import org.hibernate.annotations.NaturalId; -import org.hibernate.annotations.NaturalIdCache; - -/** - * Test case for NaturalId annotation - ANN-750 - * - * @author Emmanuel Bernard - * @author Hardy Ferentschik - */ -@Entity -@NaturalIdCache -class NaturalIdOnManyToOne { - - @Id - @GeneratedValue - int id; - - @NaturalId - @ManyToOne - Citizen citizen; - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public Citizen getCitizen() { - return citizen; - } - - public void setCitizen(Citizen citizen) { - this.citizen = citizen; - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/State.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/State.java deleted file mode 100644 index d2633fd67399..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/State.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.annotations.naturalid; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; - -/** - * @author Emmanuel Bernard - */ -@Entity -public class State { - @Id - private Integer id; - private String name; - - public State() { - } - - public State(Integer id, String name) { - this.id = id; - this.name = name; - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/identifier/MutableNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/identifier/MutableNaturalIdTest.java index 88cd8244dbb0..cabead6bfbe1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/identifier/MutableNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/identifier/MutableNaturalIdTest.java @@ -6,16 +6,15 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; - -import org.hibernate.Session; +import org.hibernate.KeyType; +import org.hibernate.NaturalIdSynchronization; import org.hibernate.annotations.NaturalId; - import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.Jpa; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; /** * @author Vlad Mihalcea @@ -35,32 +34,32 @@ public void test(EntityManagerFactoryScope scope) { }); scope.inTransaction( entityManager -> { //tag::naturalid-mutable-synchronized-example[] - //tag::naturalid-mutable-example[] - Author author = entityManager - .unwrap(Session.class) - .bySimpleNaturalId(Author.class) - .load("john@acme.com"); - //end::naturalid-mutable-example[] + Author author = entityManager.find( Author.class, + "john@acme.com", + KeyType.NATURAL ); + // change the natural id value author.setEmail("john.doe@acme.com"); - assertNull( - entityManager - .unwrap(Session.class) - .bySimpleNaturalId(Author.class) - .setSynchronizationEnabled(false) - .load("john.doe@acme.com") - ); - - assertSame(author, - entityManager - .unwrap(Session.class) - .bySimpleNaturalId(Author.class) - .setSynchronizationEnabled(true) - .load("john.doe@acme.com") - ); - //end::naturalid-mutable-example[] - + // since there has been no flush, + // the internal resolution cache + // does not know about the change - + // without synchronization, we will + // get a miss. + + Author author2 = entityManager.find( Author.class, + "john.doe@acme.com", + KeyType.NATURAL, + NaturalIdSynchronization.DISABLED ); + assertNull( author2 ); + + // with synchronization (the default), + // however, we will get correct results. + + Author author3 = entityManager.find( Author.class, + "john.doe@acme.com", + KeyType.NATURAL ); + assertEquals( author, author3 ); //end::naturalid-mutable-synchronized-example[] }); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/identifier/SimpleNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/identifier/SimpleNaturalIdTest.java index e3484a35099e..fb5fc595e055 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/identifier/SimpleNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/identifier/SimpleNaturalIdTest.java @@ -7,13 +7,19 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; +import jakarta.persistence.Timeout; +import org.hibernate.KeyType; +import org.hibernate.LockMode; import org.hibernate.Session; import org.hibernate.annotations.NaturalId; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -56,6 +62,29 @@ public void test(EntityManagerFactoryScope scope) { }); } + @Test + void testLoading(EntityManagerFactoryScope scope) { + scope.inTransaction( (entityManager) -> { + //tag::naturalid-loading-example + var book = entityManager.find( Book.class, + "978-9730228236", + KeyType.NATURAL, + LockMode.PESSIMISTIC_WRITE, + Timeout.seconds( 1 ) ); + var books = entityManager.unwrap( Session.class ).findMultiple( Book.class, + List.of("978-9730228236"), + KeyType.NATURAL, + LockMode.PESSIMISTIC_WRITE, + Timeout.seconds( 1 ) ); + //end::naturalid-loading-example + } ); + } + + @AfterEach + void tearDown(EntityManagerFactoryScope scope) { + scope.dropData(); + } + //tag::naturalid-simple-basic-attribute-mapping-example[] @Entity(name = "Book") public static class Book { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/ByteArrayNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/ByteArrayNaturalIdTest.java similarity index 97% rename from hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/ByteArrayNaturalIdTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/ByteArrayNaturalIdTest.java index 9d1231edb930..6ae19e04cccf 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/ByteArrayNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/ByteArrayNaturalIdTest.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.annotations.naturalid; +package org.hibernate.orm.test.mapping.naturalid; import org.hibernate.annotations.NaturalId; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/LoadByNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/LoadByNaturalIdTest.java index ebde39c3d614..bc6f91fa51a3 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/LoadByNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/LoadByNaturalIdTest.java @@ -5,8 +5,13 @@ package org.hibernate.orm.test.mapping.naturalid; import java.util.HashSet; +import java.util.List; import java.util.Set; +import jakarta.persistence.Timeout; +import org.hibernate.KeyType; +import org.hibernate.LockMode; +import org.hibernate.ReadOnlyMode; import org.hibernate.annotations.NaturalId; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.loader.ast.spi.NaturalIdLoader; @@ -98,6 +103,21 @@ public void testResolveIdToNaturalId(SessionFactoryScope scope) { ); } + @Test + void testFindOptions(SessionFactoryScope factoryScope) { + factoryScope.inTransaction( (session) -> { + session.find( Parent.class, "Luigi", + KeyType.NATURAL, + LockMode.PESSIMISTIC_WRITE, + Timeout.seconds( 1 ), + ReadOnlyMode.READ_ONLY ); + session.findMultiple( Parent.class, List.of("Luigi"), + KeyType.NATURAL, + LockMode.PESSIMISTIC_WRITE, + Timeout.seconds( 1 ), + ReadOnlyMode.READ_ONLY ); + } ); + } private static NaturalIdLoader getNaturalIdLoader(Class clazz, SessionImplementor session) { return session.getFactory() diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdAndAssociationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/NaturalIdAndAssociationTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdAndAssociationTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/NaturalIdAndAssociationTest.java index a1b96cc424ef..bc3daaedeaff 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdAndAssociationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/NaturalIdAndAssociationTest.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.annotations.naturalid; +package org.hibernate.orm.test.mapping.naturalid; import java.math.BigDecimal; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdOnSingleManyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/NaturalIdOnSingleManyToOneTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdOnSingleManyToOneTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/NaturalIdOnSingleManyToOneTest.java index 3a93b7da7492..804c67c3f1a5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdOnSingleManyToOneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/NaturalIdOnSingleManyToOneTest.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.annotations.naturalid; +package org.hibernate.orm.test.mapping.naturalid; import org.hibernate.annotations.NaturalId; import org.hibernate.annotations.NaturalIdCache; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/NaturalIdTest.java similarity index 82% rename from hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/NaturalIdTest.java index f9d265ddee84..1d61c6f51f86 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/NaturalIdTest.java @@ -2,12 +2,18 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.annotations.naturalid; +package org.hibernate.orm.test.mapping.naturalid; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Root; import org.hibernate.Session; +import org.hibernate.annotations.NaturalId; +import org.hibernate.annotations.NaturalIdCache; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.NaturalIdMapping; import org.hibernate.query.Query; @@ -33,7 +39,7 @@ */ @SuppressWarnings("JUnitMalformedDeclaration") @ServiceRegistry(settings = @Setting(name = USE_QUERY_CACHE, value = "true")) -@DomainModel( annotatedClasses = { Citizen.class, State.class, NaturalIdOnManyToOne.class } ) +@DomainModel( annotatedClasses = { NaturalIdTest.Citizen.class, NaturalIdTest.State.class, NaturalIdTest.NaturalIdOnManyToOne.class } ) @SessionFactory public class NaturalIdTest { @AfterEach @@ -298,4 +304,142 @@ private State getState(Session s, String name) { query.setCacheable( true ); return query.list().get( 0 ); } + + /** + * @author Emmanuel Bernard + */ + @Entity + public static class State { + @Id + private Integer id; + private String name; + + public State() { + } + + public State(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + /** + * @author Emmanuel Bernard + */ + @Entity + @NaturalIdCache + public static class Citizen { + @Id + private Integer id; + private String firstname; + private String lastname; + @NaturalId + @ManyToOne + private State state; + @NaturalId + private String ssn; + + public Citizen() { + } + + public Citizen(Integer id, String firstname, String lastname, State state, String ssn) { + this.id = id; + this.firstname = firstname; + this.lastname = lastname; + this.state = state; + this.ssn = ssn; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } + + public String getSsn() { + return ssn; + } + + public void setSsn(String ssn) { + this.ssn = ssn; + } + } + + /** + * Test case for NaturalId annotation - ANN-750 + * + * @author Emmanuel Bernard + * @author Hardy Ferentschik + */ + @Entity + @NaturalIdCache + static + class NaturalIdOnManyToOne { + + @Id + @GeneratedValue + int id; + + @NaturalId + @ManyToOne + Citizen citizen; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Citizen getCitizen() { + return citizen; + } + + public void setCitizen(Citizen citizen) { + this.citizen = citizen; + } + } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdUniqueConstraintNameTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/NaturalIdUniqueConstraintNameTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdUniqueConstraintNameTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/NaturalIdUniqueConstraintNameTest.java index 5be303072725..bf40ac9c41b6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/NaturalIdUniqueConstraintNameTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/NaturalIdUniqueConstraintNameTest.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.annotations.naturalid; +package org.hibernate.orm.test.mapping.naturalid; import jakarta.persistence.Entity; import jakarta.persistence.Id; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/BasicNaturalIdCachingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/caching/BasicNaturalIdCachingTests.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/BasicNaturalIdCachingTests.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/caching/BasicNaturalIdCachingTests.java index e9684ce967ef..3e04e7ef4e43 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/BasicNaturalIdCachingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/caching/BasicNaturalIdCachingTests.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.mapping.naturalid; +package org.hibernate.orm.test.mapping.naturalid.caching; import jakarta.persistence.Entity; import jakarta.persistence.Id; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/CompoundNaturalIdCacheTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/caching/CompoundNaturalIdCacheTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/CompoundNaturalIdCacheTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/caching/CompoundNaturalIdCacheTest.java index a0b936a1e53c..b316186d0da4 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/CompoundNaturalIdCacheTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/caching/CompoundNaturalIdCacheTest.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.mapping.naturalid.compound; +package org.hibernate.orm.test.mapping.naturalid.caching; import org.hibernate.annotations.NaturalId; import org.hibernate.annotations.NaturalIdCache; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/cid/A.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/A.java similarity index 91% rename from hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/cid/A.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/A.java index 83db358bd2a0..acf614cc12e6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/cid/A.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/A.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.annotations.naturalid.cid; +package org.hibernate.orm.test.mapping.naturalid.cid; import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/cid/AId.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/AId.java similarity index 92% rename from hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/cid/AId.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/AId.java index ff541adae413..f67d4b93115d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/cid/AId.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/AId.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.annotations.naturalid.cid; +package org.hibernate.orm.test.mapping.naturalid.cid; import jakarta.persistence.Embeddable; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/AnnotatedCompositeIdAndNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/AnnotatedCompositeIdAndNaturalIdTest.java deleted file mode 100644 index 6f295c80c616..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/AnnotatedCompositeIdAndNaturalIdTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.mapping.naturalid.cid; - -import org.hibernate.testing.orm.junit.DomainModel; -import org.hibernate.testing.orm.junit.ServiceRegistry; -import org.hibernate.testing.orm.junit.SessionFactory; -import org.hibernate.testing.orm.junit.Setting; - -import static org.hibernate.cfg.AvailableSettings.GENERATE_STATISTICS; -import static org.hibernate.cfg.AvailableSettings.USE_QUERY_CACHE; -import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE; - -/** - * @author Donnchadh O Donnabhain - */ -@ServiceRegistry( - settings = { - @Setting( name = USE_SECOND_LEVEL_CACHE, value = "false" ), - @Setting( name = USE_QUERY_CACHE, value = "false" ), - @Setting( name = GENERATE_STATISTICS, value = "false" ) - } -) -@DomainModel( annotatedClasses = { Account.class, AccountId.class } ) -@SessionFactory -public class AnnotatedCompositeIdAndNaturalIdTest extends AbstractCompositeIdAndNaturalIdTest { -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/cid/EmbeddedAndNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/EmbeddedAndNaturalIdTest.java similarity index 97% rename from hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/cid/EmbeddedAndNaturalIdTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/EmbeddedAndNaturalIdTest.java index ec9dab76b108..6ccf9c994b43 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/cid/EmbeddedAndNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/EmbeddedAndNaturalIdTest.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.annotations.naturalid.cid; +package org.hibernate.orm.test.mapping.naturalid.cid; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/HbmCompositeIdAndNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/HbmCompositeIdAndNaturalIdTest.java deleted file mode 100644 index a710d7611fd3..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/HbmCompositeIdAndNaturalIdTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.mapping.naturalid.cid; - -import org.hibernate.testing.orm.junit.DomainModel; -import org.hibernate.testing.orm.junit.ServiceRegistry; -import org.hibernate.testing.orm.junit.SessionFactory; -import org.hibernate.testing.orm.junit.Setting; - -import static org.hibernate.cfg.AvailableSettings.GENERATE_STATISTICS; -import static org.hibernate.cfg.AvailableSettings.USE_QUERY_CACHE; -import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE; - -/** - * @author Donnchadh O Donnabhain - */ -@ServiceRegistry( - settings = { - @Setting( name = USE_SECOND_LEVEL_CACHE, value = "false" ), - @Setting( name = USE_QUERY_CACHE, value = "false" ), - @Setting( name = GENERATE_STATISTICS, value = "false" ) - } -) -@DomainModel( xmlMappings = "org/hibernate/orm/test/mapping/naturalid/cid/Account.hbm.xml" ) -@SessionFactory -public class HbmCompositeIdAndNaturalIdTest extends AbstractCompositeIdAndNaturalIdTest { -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/AbstractCompositeIdAndNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/AbstractCompositeIdAndNaturalIdTest.java similarity index 75% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/AbstractCompositeIdAndNaturalIdTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/AbstractCompositeIdAndNaturalIdTest.java index 648ce07f23eb..cd4421da71e8 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/AbstractCompositeIdAndNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/AbstractCompositeIdAndNaturalIdTest.java @@ -2,40 +2,40 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.mapping.naturalid.cid; +package org.hibernate.orm.test.mapping.naturalid.composite; import org.hibernate.metamodel.mapping.AttributeMetadata; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.internal.SimpleNaturalIdMapping; import org.hibernate.persister.entity.EntityPersister; - import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.MatcherAssert.assertThat; /** * @author Steve Ebersole */ public abstract class AbstractCompositeIdAndNaturalIdTest { @Test - @JiraKey( value = "HHH-10360") + @JiraKey(value = "HHH-10360") public void testNaturalIdNullability(SessionFactoryScope scope) { - final EntityMappingType accountMapping = scope.getSessionFactory().getRuntimeMetamodels().getEntityMappingType( Account.class ); + final EntityMappingType accountMapping = scope.getSessionFactory().getRuntimeMetamodels() + .getEntityMappingType( Account.class ); final SingularAttributeMapping shortCodeMapping = ((SimpleNaturalIdMapping) accountMapping.getNaturalIdMapping()).getAttribute(); final AttributeMetadata shortCodeMetadata = shortCodeMapping.getAttributeMetadata(); assertThat( shortCodeMetadata.isNullable(), is( false ) ); final EntityPersister rootEntityPersister = accountMapping.getRootEntityDescriptor().getEntityPersister(); final int shortCodeLegacyPropertyIndex = rootEntityPersister.getPropertyIndex( "shortCode" ); - assertThat( shortCodeLegacyPropertyIndex, is ( 0 ) ); - assertThat( rootEntityPersister.getPropertyNullability()[ shortCodeLegacyPropertyIndex ], is( false ) ); + assertThat( shortCodeLegacyPropertyIndex, is( 0 ) ); + assertThat( rootEntityPersister.getPropertyNullability()[shortCodeLegacyPropertyIndex], is( false ) ); } public static final String NATURAL_ID_VALUE = "testAcct"; @@ -45,7 +45,8 @@ public void prepareTestData(SessionFactoryScope scope) { scope.inTransaction( (session) -> { // prepare some test data... - Account account = new Account( new AccountId( 1 ), NATURAL_ID_VALUE ); + Account account = new Account( + new AccountId( 1 ), NATURAL_ID_VALUE ); session.persist( account ); } ); @@ -60,7 +61,8 @@ public void cleanUpTestData(SessionFactoryScope scope) { public void testNaturalIdCriteria(SessionFactoryScope scope) { scope.inTransaction( (session) -> { - final Account account = session.bySimpleNaturalId( Account.class ).load( NATURAL_ID_VALUE ); + final Account account = session.bySimpleNaturalId( + Account.class ).load( NATURAL_ID_VALUE ); assertThat( account, notNullValue() ); } ); @@ -70,13 +72,15 @@ public void testNaturalIdCriteria(SessionFactoryScope scope) { public void testNaturalIdApi(SessionFactoryScope scope) { scope.inTransaction( (session) -> { - final Account account = session.bySimpleNaturalId( Account.class ).load( NATURAL_ID_VALUE ); + final Account account = session.bySimpleNaturalId( + Account.class ).load( NATURAL_ID_VALUE ); assertThat( account, notNullValue() ); } ); scope.inTransaction( (session) -> { - final Account account = session.byNaturalId( Account.class ).using( "shortCode", NATURAL_ID_VALUE ).load(); + final Account account = session.byNaturalId( + Account.class ).using( "shortCode", NATURAL_ID_VALUE ).load(); assertThat( account, notNullValue() ); } ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/Account.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/Account.java similarity index 86% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/Account.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/Account.java index 5f9c8c6c1c9d..e3c089a2495e 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/Account.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/Account.java @@ -2,25 +2,24 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.mapping.naturalid.cid; +package org.hibernate.orm.test.mapping.naturalid.composite; import jakarta.persistence.Basic; import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; import jakarta.persistence.Table; - import org.hibernate.annotations.NaturalId; /** * @author Donnchadh O Donnabhain */ @Entity -@Table( name = "t_acct" ) +@Table(name = "t_acct") public class Account { @EmbeddedId private AccountId accountId; - @Basic( optional = false ) + @Basic(optional = false) @NaturalId private String shortCode; @@ -31,9 +30,11 @@ public Account(AccountId accountId, String shortCode) { this.accountId = accountId; this.shortCode = shortCode; } + public String getShortCode() { return shortCode; } + public AccountId getAccountId() { return accountId; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/AccountId.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/AccountId.java similarity index 75% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/AccountId.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/AccountId.java index 6a8e518a8e45..5d58e1b5471f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/cid/AccountId.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/AccountId.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.mapping.naturalid.cid; +package org.hibernate.orm.test.mapping.naturalid.composite; import jakarta.persistence.Embeddable; @@ -20,24 +20,31 @@ protected AccountId() { public AccountId(int id) { this.id = id; } + public int intValue() { return id; } + @Override public int hashCode() { return id; } + @Override public boolean equals(Object obj) { - if (this == obj) + if ( this == obj ) { return true; - if (obj == null) + } + if ( obj == null ) { return false; - if (getClass() != obj.getClass()) + } + if ( getClass() != obj.getClass() ) { return false; + } AccountId other = (AccountId) obj; - if (other != null && id != other.id) + if ( other != null && id != other.id ) { return false; + } return true; } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/AnnotatedCompositeIdAndNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/AnnotatedCompositeIdAndNaturalIdTest.java new file mode 100644 index 000000000000..038aded0b596 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/AnnotatedCompositeIdAndNaturalIdTest.java @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.mapping.naturalid.composite; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.Setting; + +import static org.hibernate.cfg.CacheSettings.USE_QUERY_CACHE; +import static org.hibernate.cfg.CacheSettings.USE_SECOND_LEVEL_CACHE; +import static org.hibernate.cfg.StatisticsSettings.GENERATE_STATISTICS; + +/** + * @author Donnchadh O Donnabhain + */ +@ServiceRegistry( + settings = { + @Setting(name = USE_SECOND_LEVEL_CACHE, value = "false"), + @Setting(name = USE_QUERY_CACHE, value = "false"), + @Setting(name = GENERATE_STATISTICS, value = "false") + } +) +@DomainModel(annotatedClasses = {Account.class, AccountId.class}) +@SessionFactory +public class AnnotatedCompositeIdAndNaturalIdTest extends AbstractCompositeIdAndNaturalIdTest { +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/CompoundNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/CompoundNaturalIdTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/CompoundNaturalIdTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/CompoundNaturalIdTest.java index 34b0c207bdcf..ad0ef656b459 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/CompoundNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/CompoundNaturalIdTest.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.mapping.naturalid.compound; +package org.hibernate.orm.test.mapping.naturalid.composite; import org.hibernate.annotations.NaturalId; import org.hibernate.cfg.AvailableSettings; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/CompoundNaturalIdTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/CompoundNaturalIdTests.java similarity index 77% rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/CompoundNaturalIdTests.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/CompoundNaturalIdTests.java index fc1e6aed6a7e..597874c7fe36 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/CompoundNaturalIdTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/CompoundNaturalIdTests.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.mapping.naturalid; +package org.hibernate.orm.test.mapping.naturalid.composite; import java.util.HashMap; import java.util.List; @@ -13,10 +13,10 @@ import jakarta.persistence.Id; import jakarta.persistence.Table; +import org.hibernate.KeyType; import org.hibernate.NaturalIdLoadAccess; import org.hibernate.NaturalIdMultiLoadAccess; import org.hibernate.annotations.NaturalId; -import org.hibernate.loader.ast.spi.NaturalIdLoadOptions; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.metamodel.MappingMetamodel; @@ -102,30 +102,31 @@ public void testProcessing(DomainModelScope domainModelScope, SessionFactoryScop @Test public void testGetReference(SessionFactoryScope scope) { - scope.inTransaction( - session -> { - final NaturalIdLoadAccess loadAccess = session.byNaturalId( Account.class ); - loadAccess.using( "system", "matrix" ); - loadAccess.using( "username", "neo" ); - verifyEntity( loadAccess.getReference() ); - } - ); - - scope.inTransaction( - session -> { - final MappingMetamodel mappingMetamodel = session.getFactory().getRuntimeMetamodels().getMappingMetamodel(); - final EntityPersister accountMapping = mappingMetamodel.findEntityDescriptor( Account.class ); - final NaturalIdMapping naturalIdMapping = accountMapping.getNaturalIdMapping(); - - // test load by array - Object id = accountMapping.getNaturalIdLoader().resolveNaturalIdToId( VALUE_ARRAY, session ); - assertThat( id, is( 1 ) ); - - // and by Map - id = accountMapping.getNaturalIdLoader().resolveNaturalIdToId( VALUE_MAP, session ); - assertThat( id, is( 1 ) ); - } - ); + scope.inTransaction(session -> { + final MappingMetamodel mappingMetamodel = session.getFactory().getRuntimeMetamodels().getMappingMetamodel(); + final EntityPersister accountMapping = mappingMetamodel.findEntityDescriptor( Account.class ); + final NaturalIdMapping naturalIdMapping = accountMapping.getNaturalIdMapping(); + + // test load by array + var id = accountMapping.getNaturalIdLoader().resolveNaturalIdToId( + naturalIdMapping.normalizeInput( VALUE_ARRAY ), + session + ); + assertThat( id, is( 1 ) ); + + // and by Map + id = accountMapping.getNaturalIdLoader().resolveNaturalIdToId( + naturalIdMapping.normalizeInput( VALUE_MAP ), + session + ); + assertThat( id, is( 1 ) ); + + // finally, by the deprecated loader access contract + final NaturalIdLoadAccess loadAccess = session.byNaturalId( Account.class ); + loadAccess.using( "system", "matrix" ); + loadAccess.using( "username", "neo" ); + verifyEntity( loadAccess.getReference() ); + } ); } public void verifyEntity(Account accountRef) { @@ -137,29 +138,22 @@ public void verifyEntity(Account accountRef) { @Test public void testLoad(SessionFactoryScope scope) { - scope.inTransaction( - session -> { - final Account account = session.byNaturalId( Account.class ) - .using( "system", "matrix" ) - .using( "username", "neo" ) - .load(); - verifyEntity( account ); - } - ); - - scope.inTransaction( - session -> { - final MappingMetamodel mappingMetamodel = session.getFactory().getRuntimeMetamodels().getMappingMetamodel(); - final EntityPersister accountMapping = mappingMetamodel.findEntityDescriptor( Account.class ); - final NaturalIdMapping naturalIdMapping = accountMapping.getNaturalIdMapping(); - - // test load by array - accountMapping.getNaturalIdLoader().load( VALUE_ARRAY, NaturalIdLoadOptions.NONE, session ); - - // and by Map - accountMapping.getNaturalIdLoader().load( VALUE_MAP, NaturalIdLoadOptions.NONE, session ); - } - ); + scope.inTransaction(session -> { + // test load by array + var account = session.find( Account.class, VALUE_ARRAY, KeyType.NATURAL ); + verifyEntity( account ); + + // and by Map + account = session.find( Account.class, VALUE_MAP, KeyType.NATURAL ); + verifyEntity( account ); + + // finally, using the deprecated load-access contract + account = session.byNaturalId( Account.class ) + .using( "system", "matrix" ) + .using( "username", "neo" ) + .load(); + verifyEntity( account ); + } ); } @Test diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/HbmCompositeIdAndNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/HbmCompositeIdAndNaturalIdTest.java new file mode 100644 index 000000000000..9777c254e062 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/composite/HbmCompositeIdAndNaturalIdTest.java @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.mapping.naturalid.composite; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.Setting; + +import static org.hibernate.cfg.CacheSettings.USE_QUERY_CACHE; +import static org.hibernate.cfg.CacheSettings.USE_SECOND_LEVEL_CACHE; +import static org.hibernate.cfg.StatisticsSettings.GENERATE_STATISTICS; + +/** + * @author Donnchadh O Donnabhain + */ +@ServiceRegistry( + settings = { + @Setting(name = USE_SECOND_LEVEL_CACHE, value = "false"), + @Setting(name = USE_QUERY_CACHE, value = "false"), + @Setting(name = GENERATE_STATISTICS, value = "false") + } +) +@DomainModel(xmlMappings = "mappings/natural-id/composite/Account.hbm.xml") +@SessionFactory +public class HbmCompositeIdAndNaturalIdTest extends AbstractCompositeIdAndNaturalIdTest { +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/CompoundNaturalIdMappingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/CompoundNaturalIdMappingTest.java deleted file mode 100644 index ae019d64bd85..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/CompoundNaturalIdMappingTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.mapping.naturalid.compound; - -import java.util.List; - -import org.hibernate.boot.Metadata; -import org.hibernate.boot.MetadataSources; -import org.hibernate.boot.registry.StandardServiceRegistry; -import org.hibernate.boot.registry.StandardServiceRegistryBuilder; -import org.hibernate.boot.spi.MetadataImplementor; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.metamodel.mapping.EntityMappingType; -import org.hibernate.metamodel.mapping.NaturalIdMapping; -import org.hibernate.metamodel.mapping.SingularAttributeMapping; - -import org.hibernate.testing.orm.junit.JiraKey; -import org.hibernate.testing.util.ServiceRegistryUtil; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -/** - * @author Steve Ebersole - */ -@JiraKey(value = "HHH-11255") -public class CompoundNaturalIdMappingTest { - - @Test - public void test() { - final StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistry(); - - try { - Metadata meta = new MetadataSources( ssr ) - .addAnnotatedClass( PostalCarrier.class ) - .addAnnotatedClass( Country.class ) - .buildMetadata(); - ( (MetadataImplementor) meta ).orderColumns( false ); - ( (MetadataImplementor) meta ).validate(); - - final SessionFactoryImplementor sessionFactory = (SessionFactoryImplementor) meta.buildSessionFactory(); - - try { - final EntityMappingType entityMappingType = sessionFactory.getRuntimeMetamodels().getEntityMappingType( PostalCarrier.class ); - final NaturalIdMapping naturalIdMapping = entityMappingType.getNaturalIdMapping(); - final List naturalIdAttributes = naturalIdMapping.getNaturalIdAttributes(); - - assertThat( naturalIdAttributes.size(), is( 2 ) ); - assertThat( naturalIdAttributes.get( 0 ).getAttributeName(), is( "code" ) ); - assertThat( naturalIdAttributes.get( 1 ).getAttributeName(), is( "country" ) ); - } - finally { - sessionFactory.close(); - } - } - finally { - StandardServiceRegistryBuilder.destroy( ssr ); - } - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/Country.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/Country.java deleted file mode 100644 index c56d96b38fc3..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/Country.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.mapping.naturalid.compound; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; - -/** - * @author Steve Ebersole - */ -@Entity -public class Country { - @Id - private Integer id; - private String isoCode; - private String name; - - public Country() { - } - - public Country(Integer id, String isoCode, String name) { - this.id = id; - this.isoCode = isoCode; - this.name = name; - } - - public Integer getId() { - return id; - } - - private void setId(Integer id) { - this.id = id; - } - - public String getIsoCode() { - return isoCode; - } - - public void setIsoCode(String isoCode) { - this.isoCode = isoCode; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/PostalCarrier.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/PostalCarrier.java deleted file mode 100644 index da8d2494ff06..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/compound/PostalCarrier.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.mapping.naturalid.compound; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToOne; - -import org.hibernate.annotations.NaturalId; - -/** - * @author Steve Ebersole - */ -@Entity(name = "PostalCarrier") -public class PostalCarrier { - @Id - private Long id; - - @NaturalId - @ManyToOne - private Country country; - - @NaturalId - private String code; - - - public PostalCarrier() { - } - - public PostalCarrier(long id, String code, Country country) { - this.id = id; - this.code = code; - this.country = country; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public Country getCountry() { - return country; - } - - public void setCountry(Country country) { - this.country = country; - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/idclass/SimpleNaturalIdClassTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/idclass/SimpleNaturalIdClassTests.java new file mode 100644 index 000000000000..4b1ca2f1d4e0 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/idclass/SimpleNaturalIdClassTests.java @@ -0,0 +1,226 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.mapping.naturalid.idclass; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import org.hibernate.KeyType; +import org.hibernate.annotations.NaturalId; +import org.hibernate.annotations.NaturalIdClass; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * @author Steve Ebersole + */ +@SuppressWarnings("JUnitMalformedDeclaration") +@DomainModel(annotatedClasses = { + SimpleNaturalIdClassTests.OrderKey.class, + SimpleNaturalIdClassTests.Customer.class, + SimpleNaturalIdClassTests.Order.class, + SimpleNaturalIdClassTests.SystemUserKey.class, + SimpleNaturalIdClassTests.User.class, + SimpleNaturalIdClassTests.SystemUser.class +}) +@Jira( "https://hibernate.atlassian.net/browse/HHH-16383" ) +@SessionFactory(generateStatistics = true) +public class SimpleNaturalIdClassTests { + @BeforeEach + void setUp(SessionFactoryScope factoryScope) { + factoryScope.inTransaction( (session) -> { + session.persist( new SystemUser( 1, "steve", "ci", "abc" ) ); + session.persist( new User( 1, "steve", "def" ) ); + + final Customer billys = new Customer( 1, "bILLY bOB'S Steak House and Grill and Bar and Sushi" ); + session.persist( billys ); + + final Order billys1001 = new Order( 1, billys, 1001, Instant.now() ); + session.persist( billys1001 ); + } ); + } + + @AfterEach + void tearDown(SessionFactoryScope factoryScope) { + factoryScope.dropData(); + } + + @Test + void testFindBySimple(SessionFactoryScope factoryScope) { + factoryScope.inTransaction( (session) -> { + var result = session.find( User.class, "steve", KeyType.NATURAL ); + assertEquals( 1, result.id ); + } ); + } + + @Test + void testFindMultipleBySimple(SessionFactoryScope factoryScope) { + // baseline + factoryScope.inTransaction( (session) -> { + var results = session.findMultiple( User.class, List.of( 1, 2 ), KeyType.IDENTIFIER ); + assertThat( results ).hasSize( 2 ); + assertEquals( 1, results.get( 0 ).id ); + assertNull( results.get( 1 ) ); + } ); + + factoryScope.inTransaction( (session) -> { + var results = session.findMultiple( User.class, List.of( "steve", "john" ), KeyType.NATURAL ); + assertThat( results ).hasSize( 2 ); + assertEquals( 1, results.get( 0 ).id ); + assertNull( results.get( 1 ) ); + } ); + } + + @Test + void testFindByClass(SessionFactoryScope factoryScope) { + factoryScope.inTransaction( (session) -> { + var result = session.find( SystemUser.class, new SystemUserKey("steve", "ci"), KeyType.NATURAL ); + assertEquals( 1, result.id ); + } ); + } + + @Test + void testFindMultipleByClass(SessionFactoryScope factoryScope) { + factoryScope.inTransaction( (session) -> { + var results = session.findMultiple( SystemUser.class, List.of( new SystemUserKey("steve", "ci") ), KeyType.NATURAL ); + assertThat( results ).hasSize( 1 ); + assertEquals( 1, results.get( 0 ).id ); + } ); + } + + @Test + void testNormalization(SessionFactoryScope factoryScope) { + var stats = factoryScope.getSessionFactory().getStatistics(); + stats.clear(); + + factoryScope.inTransaction( (session) -> { + session.find( SystemUser.class, new SystemUserKey("steve", "ci"), KeyType.NATURAL ); + assertEquals( 1, stats.getNaturalIdStatistics( SystemUser.class.getName() ).getNormalizationCount() ); + } ); + } + + @Test + void testFindByClassWithToOne(SessionFactoryScope factoryScope) { + factoryScope.inTransaction( (session) -> { + // the current reality is that we need a reference to the associated entity + // rather than just its id + var customer = session.getReference( Customer.class, 1 ); + session.find( Order.class, new OrderKey(customer, 1001), KeyType.NATURAL ); + } ); + } + + @SuppressWarnings("FieldCanBeLocal") + @Entity(name="User") + @Table(name="t_users") + public static class User { + @Id + private Integer id; + @NaturalId + private String username; + private String stuff; + + public User() { + } + + public User(Integer id, String username, String stuff) { + this.id = id; + this.username = username; + this.stuff = stuff; + } + } + + public record SystemUserKey(String system, String username) { + } + + @SuppressWarnings("FieldCanBeLocal") + //tag::naturalidclass-mapping-example[] + @Entity(name="SystemUser") + @Table(name="t_sys_users") + @NaturalIdClass(SystemUserKey.class) + public static class SystemUser { + @Id + private Integer id; + @NaturalId + @Column(name = "sys") + private String system; + @NaturalId + private String username; + //end::naturalidclass-mapping-example[] + private String stuff; + + public SystemUser() { + } + + public SystemUser(Integer id, String system, String username, String stuff) { + this.id = id; + this.system = system; + this.username = username; + this.stuff = stuff; + } + //tag::naturalidclass-mapping-example[] + } + //end::naturalidclass-mapping-example[] + + @SuppressWarnings("FieldCanBeLocal") + @Entity(name="Customer") + @Table(name="customers") + public static class Customer { + @Id + private Integer id; + private String name; + + public Customer() { + } + + public Customer(Integer id, String name) { + this.id = id; + this.name = name; + } + } + + public record OrderKey(Customer customer, int invoiceNumber) { + } + + @SuppressWarnings("FieldCanBeLocal") + @Entity(name="Order") + @Table(name="orders") + @NaturalIdClass(OrderKey.class) + public static class Order { + @Id + private Integer id; + @NaturalId + @ManyToOne + @JoinColumn(name = "customer_fk") + private Customer customer; + @NaturalId + int invoiceNumber; + private Instant timestamp; + + public Order() { + } + + public Order(Integer id, Customer customer, int invoiceNumber, Instant timestamp) { + this.id = id; + this.customer = customer; + this.invoiceNumber = invoiceNumber; + this.timestamp = timestamp; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/ImmutableManyToOneNaturalIdHbmTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/ImmutableManyToOneNaturalIdHbmTest.java index 81716bff10c8..978352d75d69 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/ImmutableManyToOneNaturalIdHbmTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/ImmutableManyToOneNaturalIdHbmTest.java @@ -41,7 +41,7 @@ @Setting( name = Environment.GENERATE_STATISTICS, value = "true" ) } ) -@DomainModel( xmlMappings = "org/hibernate/orm/test/mapping/naturalid/immutable/ParentChildWithManyToOne.hbm.xml" ) +@DomainModel( xmlMappings = "mappings/natural-id/immutable/ParentChildWithManyToOne.hbm.xml" ) @SessionFactory public class ImmutableManyToOneNaturalIdHbmTest { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/ImmutableNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/ImmutableNaturalIdTest.java index 1280a47e1bbf..0802b0ebe7b5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/ImmutableNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/ImmutableNaturalIdTest.java @@ -6,6 +6,7 @@ import jakarta.persistence.PersistenceException; +import org.hibernate.KeyType; import org.hibernate.cfg.AvailableSettings; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.AttributeMapping; @@ -22,6 +23,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -38,7 +40,7 @@ @Setting( name = AvailableSettings.GENERATE_STATISTICS, value = "true" ) } ) -@DomainModel( xmlMappings = "org/hibernate/orm/test/mapping/naturalid/immutable/User.hbm.xml" ) +@DomainModel( xmlMappings = "mappings/natural-id/immutable/User.hbm.xml" ) @SessionFactory public class ImmutableNaturalIdTest { @AfterEach @@ -83,6 +85,34 @@ public void testNaturalIdCheck(SessionFactoryScope scope) { } } + @Test + public void testNaturalIdCheckManaged(SessionFactoryScope scope) { + scope.inTransaction( + (session) -> { + final User user = new User( "steve", "superSecret" ); + session.persist( user ); + } + ); + + scope.inTransaction( (session) -> { + var user = session.find( User.class, "steve", KeyType.NATURAL ); + + // try to change the natural-id... should error on flush + user.setUserName( "Steve" ); + + try { + session.flush(); + fail(); + } + catch (PersistenceException expected) { + assertThat( expected ).hasMessageContainingAll( + "An immutable natural identifier", + "was altered" + ); + } + } ); + } + @Test public void testNaturalIdCache(SessionFactoryScope scope) { final StatisticsImplementor statistics = scope.getSessionFactory().getStatistics(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/ImmutableNaturalKeyLookupTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/ImmutableNaturalKeyLookupTest.java similarity index 79% rename from hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/ImmutableNaturalKeyLookupTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/ImmutableNaturalKeyLookupTest.java index c1bcbc477388..302d5e4bd74d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/naturalid/ImmutableNaturalKeyLookupTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/immutable/ImmutableNaturalKeyLookupTest.java @@ -2,10 +2,21 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Red Hat Inc. and Hibernate Authors */ -package org.hibernate.orm.test.annotations.naturalid; +package org.hibernate.orm.test.mapping.naturalid.immutable; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.FlushModeType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Version; import org.hibernate.Session; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.NaturalId; +import org.hibernate.annotations.NaturalIdCache; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.RootGraph; @@ -19,6 +30,9 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import java.util.HashSet; +import java.util.Set; + import static org.hibernate.cfg.CacheSettings.USE_QUERY_CACHE; import static org.hibernate.cfg.StatisticsSettings.GENERATE_STATISTICS; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -32,7 +46,10 @@ @Setting(name=GENERATE_STATISTICS, value="true"), @Setting(name=USE_QUERY_CACHE, value="true") }) -@DomainModel(annotatedClasses = {A.class, D.class}) +@DomainModel(annotatedClasses = { + ImmutableNaturalKeyLookupTest.A.class, + ImmutableNaturalKeyLookupTest.D.class +}) @SessionFactory public class ImmutableNaturalKeyLookupTest { @AfterEach @@ -282,5 +299,86 @@ private A fetchA(Session s, String fetch) { return a; } + @Entity + @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) + @NaturalIdCache + public static class A { + + @Id + @GeneratedValue(strategy = GenerationType.TABLE) + private long oid; + + @Version + private int version; + + @Column + @NaturalId(mutable = false) + private String name; + + @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) + @org.hibernate.annotations.OptimisticLock(excluded = true) + @jakarta.persistence.OneToMany(mappedBy = "a") + private Set ds = new HashSet(); + + @jakarta.persistence.OneToOne + private D singleD = null; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getDs() { + return ds; + } + + public void setDs(Set ds) { + this.ds = ds; + } + + public D getSingleD() { + return singleD; + } + public void setSingleD(D singleD) { + this.singleD = singleD; + } + + } + + @Entity + @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) + public static class D { + @Id + @GeneratedValue(strategy = GenerationType.TABLE) + public long oid; + + @Version + private int version; + + @jakarta.persistence.ManyToOne(fetch = FetchType.LAZY) + private A a = null; + + @jakarta.persistence.OneToOne(mappedBy = "singleD") + private A singleA = null; + + public A getA() { + return a; + } + + public void setA(A a) { + this.a = a; + } + + public A getSingleA() { + return singleA; + } + + public void setSingleA(A singleA) { + this.singleA = singleA; + } + } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java index 0bca803ce384..445ebde56018 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/mutable/MutableNaturalIdTest.java @@ -24,8 +24,8 @@ import static org.hibernate.cfg.AvailableSettings.GENERATE_STATISTICS; import static org.hibernate.cfg.AvailableSettings.USE_QUERY_CACHE; import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE; +import static org.junit.Assert.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; @@ -41,7 +41,7 @@ @Setting( name = GENERATE_STATISTICS, value = "true" ), } ) -@DomainModel( xmlMappings = "org/hibernate/orm/test/mapping/naturalid/mutable/User.hbm.xml" ) +@DomainModel( xmlMappings = "mappings/natural-id/mutable/User.hbm.xml" ) @SessionFactory public class MutableNaturalIdTest { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/nullable/NullableNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/nullable/NullableNaturalIdTest.java index 63e698c6bf7d..15f2c2ac96c6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/nullable/NullableNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/naturalid/nullable/NullableNaturalIdTest.java @@ -4,6 +4,7 @@ */ package org.hibernate.orm.test.mapping.naturalid.nullable; +import org.hibernate.KeyType; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.NaturalIdMapping; import org.hibernate.metamodel.mapping.SingularAttributeMapping; @@ -14,6 +15,7 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.hamcrest.Matchers.is; @@ -28,7 +30,7 @@ */ @DomainModel( annotatedClasses = { A.class, B.class, C.class, D.class }, - xmlMappings = "/org/hibernate/orm/test/mapping/naturalid/nullable/User.hbm.xml" + xmlMappings = "mappings/natural-id/nullable/User.hbm.xml" ) @SessionFactory public class NullableNaturalIdTest { @@ -197,6 +199,22 @@ public void testUniqueAssociation(SessionFactoryScope scope) { ); } + @Test + void testNullability(SessionFactoryScope factoryScope) { + factoryScope.inTransaction( (session) -> { + session.persist( new C( 1, null ) ); + session.persist( new C( 2, "something" ) ); + } ); + + factoryScope.inTransaction( (session) -> { + final C c1 = session.find( C.class, null, KeyType.NATURAL ); + Assertions.assertEquals( 1, c1.oid ); + + final C c2 = session.find( C.class, "something", KeyType.NATURAL ); + Assertions.assertEquals( 2, c2.oid ); + } ); + } + @Test public void testNaturalIdQuerySupportingNullValues(SessionFactoryScope scope) { scope.inTransaction( diff --git a/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/cid/Account.hbm.xml b/hibernate-core/src/test/resources/mappings/natural-id/composite/Account.hbm.xml similarity index 94% rename from hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/cid/Account.hbm.xml rename to hibernate-core/src/test/resources/mappings/natural-id/composite/Account.hbm.xml index f629b1b3c0c8..39819682a3f7 100644 --- a/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/cid/Account.hbm.xml +++ b/hibernate-core/src/test/resources/mappings/natural-id/composite/Account.hbm.xml @@ -5,7 +5,7 @@ --> - + diff --git a/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/immutable/ParentChildWithManyToOne.hbm.xml b/hibernate-core/src/test/resources/mappings/natural-id/immutable/ParentChildWithManyToOne.hbm.xml similarity index 100% rename from hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/immutable/ParentChildWithManyToOne.hbm.xml rename to hibernate-core/src/test/resources/mappings/natural-id/immutable/ParentChildWithManyToOne.hbm.xml diff --git a/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/immutable/User.hbm.xml b/hibernate-core/src/test/resources/mappings/natural-id/immutable/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/immutable/User.hbm.xml rename to hibernate-core/src/test/resources/mappings/natural-id/immutable/User.hbm.xml diff --git a/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/mutable/User.hbm.xml b/hibernate-core/src/test/resources/mappings/natural-id/mutable/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/mutable/User.hbm.xml rename to hibernate-core/src/test/resources/mappings/natural-id/mutable/User.hbm.xml diff --git a/hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/nullable/User.hbm.xml b/hibernate-core/src/test/resources/mappings/natural-id/nullable/User.hbm.xml similarity index 100% rename from hibernate-core/src/test/resources/org/hibernate/orm/test/mapping/naturalid/nullable/User.hbm.xml rename to hibernate-core/src/test/resources/mappings/natural-id/nullable/User.hbm.xml diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/util/ast/HqlHelper.java b/hibernate-testing/src/main/java/org/hibernate/testing/util/ast/HqlHelper.java index e3f93cd99048..fa07a980efb3 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/util/ast/HqlHelper.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/util/ast/HqlHelper.java @@ -38,11 +38,11 @@ import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.ast.tree.update.UpdateStatement; import org.hibernate.sql.exec.internal.JdbcOperationQueryDelete; -import org.hibernate.sql.exec.internal.JdbcOperationQuerySelect; import org.hibernate.sql.exec.internal.JdbcOperationQueryUpdate; import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert; import org.hibernate.sql.exec.spi.JdbcParameterBindings; +import org.hibernate.sql.exec.spi.JdbcSelect; /// Utilities for helping test HQL translation /// @@ -190,7 +190,7 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter extends SqmInterpreter, SelectStatement, JdbcOperationQuerySelect> { + private static class SqmSelectInterpreter extends SqmInterpreter, SelectStatement, JdbcSelect> { public SqmSelectInterpreter( String hql, SessionFactoryImplementor sessionFactory) { @@ -214,7 +214,7 @@ protected SqmTranslator createSqmTranslator( } @Override - protected SqlAstTranslator createSqlAstTranslator( + protected SqlAstTranslator createSqlAstTranslator( SqmTranslation sqmTranslation) { final SqlAstTranslatorFactory sqlAstTranslatorFactory = sessionFactory .getJdbcServices() diff --git a/whats-new.adoc b/whats-new.adoc index f2bcd6fe6baf..fadab9078a3d 100644 --- a/whats-new.adoc +++ b/whats-new.adoc @@ -11,3 +11,71 @@ Describes the new features and capabilities added to Hibernate ORM in {version}. IMPORTANT: If migrating from earlier versions, be sure to also check out the link:{migrationGuide}[Migration Guide] for discussion of impactful changes. + +[[key-type]] +== KeyType + +The new `KeyType` enum is a `FindOption` that allows `find()` and `findMultiple()` to perform a load based on natural id in addition to identifier. + +[source,java] +---- +@Entity +class Person { + @Id + Integer id; + @NaturalId + String name; + ... +} + +// loads by id, implicitly +session.find( Person.class, 1 ); + +// loads by id, explicitly +session.find( Person.class, 1, KeyType.IDENTIFIER ); + +// loads by natural-id +session.find( Person.class, "Bilbo Baggins", KeyType.NATURAL ); + +// load by multiple natural-ids +session.findMultiple( Person.class, + List.of( "Bilbo Baggins", "Gandolph the Grey" ), + KeyType.NATURAL ); +---- + + +[[natural-id-class]] +== @NaturalIdClass + +The new `@NaturalIdClass` annotation models a non-aggregated composite natural id for the purpose of loading. +It acts very much like `@IdClass` does for identifier loading. + +[source,java] +---- +@Entity +@NaturalIdClass(PersonNameKey.class) +class Person { + @Id + Integer id; + @NaturalId + String firstName; + @NaturalId + String lastName; + ... +} + +class PersonNameKey { + String firstName; + String lastName; + ... +} +---- + +Given this model, we can load Person by natural id via: + +[source,java] +---- +session.find( Order.class, + new PersonNameKey( "Bilbo", "Baggins" ), + KeyType.NATURAL ) +---- From 67d35af9ea621b431642e900acac001055dd0aa9 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 11 Dec 2025 16:53:37 -0700 Subject: [PATCH 087/181] HHH-6598 - Immutable entities should not have up-to-date checks performed on a flush --- .../DefaultFlushEntityEventListener.java | 6 ++ .../ImmutableDirtinessCheckingTests.java | 88 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/immutable/ImmutableDirtinessCheckingTests.java diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java index 50b4a23aaccb..205df311feba 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java @@ -120,6 +120,12 @@ public void onFlushEntity(FlushEntityEvent event) throws HibernateException { final var entry = event.getEntityEntry(); final var session = event.getSession(); + // short-circuit for immutable entities.... + if ( !entry.getPersister().isMutable() && !entry.getPersister().hasCollections() ) { + // nothing to do + return; + } + final boolean mightBeDirty = entry.requiresDirtyCheck( entity ); final Object[] values = getValues( entity, entry, mightBeDirty, session ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/ImmutableDirtinessCheckingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/ImmutableDirtinessCheckingTests.java new file mode 100644 index 000000000000..16448777cf41 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/immutable/ImmutableDirtinessCheckingTests.java @@ -0,0 +1,88 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.immutable; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import org.hibernate.annotations.Immutable; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +@DomainModel(annotatedClasses = ImmutableDirtinessCheckingTests.ReferenceData.class) +@SessionFactory +public class ImmutableDirtinessCheckingTests { + @Test + void testDirtyCheckingBehavior(SessionFactoryScope factoryScope) { + factoryScope.inTransaction( (session) -> { + var data = session.find( ReferenceData.class, 1 ); + data.setName( "another value" ); + nameAccessCount = 0; + session.flush(); + assertThat( nameAccessCount ).isEqualTo( 0 ); + } ); + + factoryScope.inTransaction( (session) -> { + var data = session.find( ReferenceData.class, 1 ); + assertThat( data.getName() ).isEqualTo( "initial value" ); + } ); + } + + @BeforeEach + void prepareTestData(SessionFactoryScope factoryScope) { + factoryScope.inTransaction( (session) -> { + session.persist( new ReferenceData( 1, "initial value" ) ); + } ); + } + + @Test + void dropTestData(SessionFactoryScope factoryScope) { + factoryScope.dropData(); + } + + private static int nameAccessCount = 0; + + @Entity(name="ReferenceData") + @Table(name="ReferenceData") + @Immutable + public static class ReferenceData { + private Integer id; + private String name; + + public ReferenceData() { + } + + public ReferenceData(Integer id, String name) { + this.id = id; + this.name = name; + } + + @Id + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + nameAccessCount++; + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} From b6748cda18670d7263db6b302cba2d2e89b66347 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 10 Dec 2025 17:25:30 +0100 Subject: [PATCH 088/181] Use DateJavaType instead of JdbcXxxxJavaTypes to represent fields of type Date This fixes bugs in the metamodel where getJavaType() would return the wrong class even in the Persistence-standard metamodel. It's also a first step to fixing a bunch of other unsound things we do in the codebase. --- .../internal/InferredBasicValueResolver.java | 3 +- .../domain/internal/AbstractAttribute.java | 7 +- .../tree/select/SqmDynamicInstantiation.java | 24 +- .../DynamicInstantiationResultImpl.java | 14 +- .../hibernate/type/StandardBasicTypes.java | 65 +++++- .../java/AbstractTemporalJavaType.java | 2 +- .../descriptor/java/CalendarDateJavaType.java | 3 +- .../descriptor/java/CalendarTimeJavaType.java | 4 +- .../type/descriptor/java/DateJavaType.java | 211 +++++++++++------- .../descriptor/java/JdbcDateJavaType.java | 81 ++++--- .../descriptor/java/JdbcTimeJavaType.java | 83 +++---- .../java/JdbcTimestampJavaType.java | 117 +++++----- .../descriptor/jdbc/JdbcTypeIndicators.java | 4 +- .../hibernate/type/spi/TypeConfiguration.java | 4 +- 14 files changed, 371 insertions(+), 251 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java index 7e0b38521d3d..ef4a1c8e54f8 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/InferredBasicValueResolver.java @@ -541,7 +541,7 @@ public static BasicValue.Resolution fromTemporal( ? reflectedJtd.resolveTypeForPrecision( requestedTemporalPrecision, typeConfiguration ) // Avoid using the DateJavaType and prefer the JdbcTimestampJavaType : reflectedJtd.resolveTypeForPrecision( reflectedJtd.getPrecision(), typeConfiguration ); - final BasicType jdbcMapping = basicTypeRegistry.resolve( temporalJavaType, explicitJdbcType ); + final var jdbcMapping = basicTypeRegistry.resolve( temporalJavaType, explicitJdbcType ); return new InferredBasicValueResolution<>( jdbcMapping, temporalJavaType, @@ -568,7 +568,6 @@ public static BasicValue.Resolution fromTemporal( } else { basicType = basicTypeRegistry.resolve( - // Avoid using the DateJavaType and prefer the JdbcTimestampJavaType reflectedJtd.resolveTypeForPrecision( reflectedJtd.getPrecision(), typeConfiguration ), reflectedJtd.getRecommendedJdbcType( stdIndicators ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractAttribute.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractAttribute.java index 22b999de5b4c..55c45407641a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/AbstractAttribute.java @@ -63,8 +63,11 @@ public String getName() { @Override public Class getJavaType() { - return valueType instanceof BasicTypeImpl basicType - ? basicType.getJavaType() + // TODO: create a new method to abstract this logic + return valueType instanceof BasicTypeImpl basicType + // handles primitives in basic types + ? (Class) basicType.getJavaType() + // good for everything else : attributeJtd.getJavaTypeClass(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java index 13b1675ae760..c8e79168c930 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmDynamicInstantiation.java @@ -5,7 +5,6 @@ package org.hibernate.query.sqm.tree.select; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -23,11 +22,15 @@ import org.hibernate.query.sqm.tree.domain.SqmDomainType; import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.query.sqm.tree.jpa.AbstractJpaSelection; +import org.hibernate.type.descriptor.java.DateJavaType; import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.java.TemporalJavaType; import org.hibernate.type.spi.TypeConfiguration; import org.jboss.logging.Logger; +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; import static java.util.stream.Collectors.toList; import static org.hibernate.internal.util.NullnessUtil.castNonNull; import static org.hibernate.query.sqm.DynamicInstantiationNature.CLASS; @@ -157,7 +160,7 @@ public boolean checkInstantiation(TypeConfiguration typeConfiguration) { if ( isConstructorCompatible( javaType, argTypes, typeConfiguration ) ) { return true; } - final List> arguments = getArguments(); + final var arguments = getArguments(); final List aliases = new ArrayList<>( arguments.size() ); for ( var argument : arguments ) { final String alias = argument.getAlias(); @@ -182,9 +185,18 @@ private List> argumentTypes() { return getArguments().stream() .map( arg -> { final var expressible = arg.getExpressible(); - return expressible != null && expressible.getExpressibleJavaType() != null ? - expressible.getExpressibleJavaType().getJavaTypeClass() : - Void.class; + if ( expressible != null ) { + final var expressibleJavaType = expressible.getExpressibleJavaType(); + if ( expressibleJavaType != null ) { + return expressibleJavaType instanceof DateJavaType temporalJavaType + // Hack to accommodate a constructor with java.sql parameter + // types when the entity has java.util.Date as its field types. + // (This was requested in HHH-4179 and we fixed it by accident.) + ? TemporalJavaType.resolveJavaTypeClass( temporalJavaType.getPrecision() ) + : expressibleJavaType.getJavaTypeClass(); + } + } + return Void.class; } ).collect( toList() ); } @@ -227,7 +239,7 @@ public SqmDynamicInstantiationTarget getInstantiationTarget() { } public List> getArguments() { - return arguments == null ? Collections.emptyList() : Collections.unmodifiableList( arguments ); + return arguments == null ? emptyList() : unmodifiableList( arguments ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationResultImpl.java index 58ac3dd808e1..58294dad65ac 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/instantiation/internal/DynamicInstantiationResultImpl.java @@ -17,8 +17,10 @@ import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.InitializerParent; import org.hibernate.sql.results.graph.instantiation.DynamicInstantiationResult; +import org.hibernate.type.descriptor.java.DateJavaType; import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.java.TemporalJavaType; import org.jboss.logging.Logger; import static java.util.stream.Collectors.toList; @@ -157,7 +159,7 @@ private DomainResultAssembler assembler( final var constructor = findMatchingConstructor( javaType.getJavaTypeClass(), argumentReaders.stream() - .map( reader -> reader.getAssembledJavaType().getJavaTypeClass() ) + .map( reader -> argumentClass( reader ) ) .collect( toList() ), creationState.getSqlAstCreationContext() .getMappingMetamodel() @@ -194,6 +196,16 @@ private DomainResultAssembler assembler( return new DynamicInstantiationAssemblerInjectionImpl<>( javaType, argumentReaders ); } + private static Class argumentClass(ArgumentReader reader) { + final var assembledJavaType = reader.getAssembledJavaType(); + return assembledJavaType instanceof DateJavaType temporalJavaType + // Hack to accommodate a constructor with java.sql parameter + // types when the entity has java.util.Date as its field types. + // (This was requested in HHH-4179 and we fixed it by accident.) + ? TemporalJavaType.resolveJavaTypeClass( temporalJavaType.getPrecision() ) + : assembledJavaType.getJavaTypeClass(); + } + private List signature() { return argumentResults.stream() .map( adt -> adt.getResultJavaType().getTypeName() ) diff --git a/hibernate-core/src/main/java/org/hibernate/type/StandardBasicTypes.java b/hibernate-core/src/main/java/org/hibernate/type/StandardBasicTypes.java index 7966eced3ece..7f58fcea8d8c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/StandardBasicTypes.java +++ b/hibernate-core/src/main/java/org/hibernate/type/StandardBasicTypes.java @@ -11,8 +11,6 @@ import java.sql.Blob; import java.sql.Clob; import java.sql.NClob; -import java.sql.Time; -import java.sql.Timestamp; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; @@ -493,7 +491,7 @@ private StandardBasicTypes() { */ public static final BasicTypeReference TIME = new BasicTypeReference<>( "time", - Time.class, + java.util.Date.class, SqlTypes.TIME ); @@ -503,7 +501,7 @@ private StandardBasicTypes() { */ public static final BasicTypeReference DATE = new BasicTypeReference<>( "date", - java.sql.Date.class, + java.util.Date.class, SqlTypes.DATE ); @@ -513,7 +511,37 @@ private StandardBasicTypes() { */ public static final BasicTypeReference TIMESTAMP = new BasicTypeReference<>( "timestamp", - Timestamp.class, + java.util.Date.class, + SqlTypes.TIMESTAMP + ); + + /** + * The standard Hibernate type for mapping {@link java.sql.Time} to JDBC + * {@link org.hibernate.type.SqlTypes#TIMESTAMP TIMESTAMP}. + */ + public static final BasicTypeReference SQL_TIME = new BasicTypeReference<>( + "sql_time", + java.sql.Time.class, + SqlTypes.TIME + ); + + /** + * The standard Hibernate type for mapping {@link java.sql.Date} to JDBC + * {@link org.hibernate.type.SqlTypes#DATE DATE}. + */ + public static final BasicTypeReference SQL_DATE = new BasicTypeReference<>( + "sql_date", + java.sql.Date.class, + SqlTypes.DATE + ); + + /** + * The standard Hibernate type for mapping {@link java.sql.Timestamp} to JDBC + * {@link org.hibernate.type.SqlTypes#TIMESTAMP TIMESTAMP}. + */ + public static final BasicTypeReference SQL_TIMESTAMP = new BasicTypeReference<>( + "sql_timestamp", + java.sql.Timestamp.class, SqlTypes.TIMESTAMP ); @@ -1173,21 +1201,42 @@ public static void prime(TypeConfiguration typeConfiguration) { DATE, "org.hibernate.type.DateType", basicTypeRegistry, - "date", java.sql.Date.class.getName() + "date" ); handle( TIME, "org.hibernate.type.TimeType", basicTypeRegistry, - "time", java.sql.Time.class.getName() + "time" ); handle( TIMESTAMP, "org.hibernate.type.TimestampType", basicTypeRegistry, - "timestamp", java.sql.Timestamp.class.getName(), Date.class.getName() + "timestamp", Date.class.getName() + ); + + handle( + SQL_DATE, + null, + basicTypeRegistry, + "sql_date", java.sql.Date.class.getName() + ); + + handle( + SQL_TIME, + null, + basicTypeRegistry, + "sql_time", java.sql.Time.class.getName() + ); + + handle( + SQL_TIMESTAMP, + null, + basicTypeRegistry, + "sql_timestamp", java.sql.Timestamp.class.getName() ); handle( diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractTemporalJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractTemporalJavaType.java index d1a0079326ce..914eb3df9d2e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractTemporalJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractTemporalJavaType.java @@ -33,7 +33,7 @@ public AbstractTemporalJavaType( } @Override - public final TemporalJavaType resolveTypeForPrecision( + public TemporalJavaType resolveTypeForPrecision( TemporalType precision, TypeConfiguration typeConfiguration) { if ( precision == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java index 336041f99ba0..7e807a15dbbd 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java @@ -56,7 +56,8 @@ protected TemporalJavaType forTimePrecision(TypeConfiguration typeConfigu } public String toString(Calendar value) { - return JdbcDateJavaType.INSTANCE.toString( value.getTime() ); + return JdbcDateJavaType.INSTANCE.toString( + new java.sql.Date( value.getTime().getTime() ) ); } public Calendar fromString(CharSequence string) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java index e544c30f2e2c..5c7acd663c3a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java @@ -4,6 +4,7 @@ */ package org.hibernate.type.descriptor.java; +import java.sql.Time; import java.sql.Types; import java.util.Calendar; import java.util.Date; @@ -56,7 +57,8 @@ protected TemporalJavaType forDatePrecision(TypeConfiguration typeConfigu } public String toString(Calendar value) { - return JdbcTimeJavaType.INSTANCE.toString( value.getTime() ); + return JdbcTimeJavaType.INSTANCE.toString( + new Time( value.getTime().getTime() ) ); } public Calendar fromString(CharSequence string) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java index bed568b2643b..a8b2b9eccf8d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java @@ -4,18 +4,14 @@ */ package org.hibernate.type.descriptor.java; +import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; -import java.time.format.DateTimeParseException; -import java.time.temporal.ChronoField; -import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; -import java.util.GregorianCalendar; import jakarta.persistence.TemporalType; -import org.hibernate.HibernateException; import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.type.descriptor.WrapperOptions; @@ -23,6 +19,8 @@ import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; import org.hibernate.type.spi.TypeConfiguration; +import static org.hibernate.type.descriptor.java.JdbcDateJavaType.toDateEpoch; + /** * Descriptor for {@link Date} handling. * @@ -30,17 +28,45 @@ */ public class DateJavaType extends AbstractTemporalJavaType implements VersionJavaType { public static final DateJavaType INSTANCE = new DateJavaType(); + private final TemporalType precision; public static class DateMutabilityPlan extends MutableMutabilityPlan { - public static final DateMutabilityPlan INSTANCE = new DateMutabilityPlan(); + public static final DateMutabilityPlan INSTANCE = new DateMutabilityPlan( TemporalType.TIMESTAMP ); + private final TemporalType precision; + + public DateMutabilityPlan(TemporalType precision) { + this.precision = precision; + } + @Override public Date deepCopyNotNull(Date value) { - return new Date( value.getTime() ); + if ( value instanceof java.sql.Timestamp timestamp ) { + return JdbcTimestampJavaType.TimestampMutabilityPlan.INSTANCE.deepCopyNotNull( timestamp ); + } + else if ( value instanceof java.sql.Date date ) { + return JdbcDateJavaType.DateMutabilityPlan.INSTANCE.deepCopyNotNull( date ); + } + else if ( value instanceof java.sql.Time time ) { + return JdbcTimeJavaType.TimeMutabilityPlan.INSTANCE.deepCopyNotNull( time ); + } + else { + return switch ( precision ) { + case TIMESTAMP -> wrapSqlTimestamp( value ); + case DATE -> wrapSqlDate( value ); + case TIME -> wrapSqlTime( value ); + }; + } } } public DateJavaType() { super( Date.class, DateMutabilityPlan.INSTANCE ); + this.precision = TemporalType.TIMESTAMP; + } + + private DateJavaType(TemporalType precision) { + super( Date.class, new DateMutabilityPlan(precision) ); + this.precision = precision; } @Override @@ -55,18 +81,30 @@ public Date cast(Object value) { @Override public TemporalType getPrecision() { - return TemporalType.TIMESTAMP; + return precision; + } + + @Override + public TemporalJavaType resolveTypeForPrecision(TemporalType precision, TypeConfiguration typeConfiguration) { + return (TemporalJavaType) new DateJavaType( precision ); } @Override public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { - // this "Date" is really a timestamp - return dialect.getDefaultTimestampPrecision(); + return switch ( precision ) { + case TIMESTAMP -> JdbcTimestampJavaType.INSTANCE.getDefaultSqlPrecision( dialect, jdbcType ); + case DATE -> JdbcDateJavaType.INSTANCE.getDefaultSqlPrecision( dialect, jdbcType ); + case TIME -> JdbcTimeJavaType.INSTANCE.getDefaultSqlPrecision( dialect, jdbcType ); + }; } @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { - return context.getJdbcType( Types.TIMESTAMP ); + return context.getJdbcType( switch ( precision ) { + case TIMESTAMP -> Types.TIMESTAMP; + case DATE -> Types.DATE; + case TIME -> Types.TIME; + } ); } @Override @SuppressWarnings("unchecked") @@ -86,21 +124,31 @@ protected TemporalJavaType forTimePrecision(TypeConfiguration typeConfigu @Override public String toString(Date value) { - return JdbcTimestampJavaType.LITERAL_FORMATTER.format( value.toInstant() ); +// return JdbcTimestampJavaType.LITERAL_FORMATTER.format( value.toInstant() ); + return switch ( precision ) { + case TIMESTAMP -> JdbcTimestampJavaType.INSTANCE.toString( wrapSqlTimestamp( value ) ); + case DATE -> JdbcDateJavaType.INSTANCE.toString( wrapSqlDate( value ) ); + case TIME -> JdbcTimeJavaType.INSTANCE.toString( wrapSqlTime( value ) ); + }; } @Override public Date fromString(CharSequence string) { - try { - final TemporalAccessor accessor = JdbcTimestampJavaType.LITERAL_FORMATTER.parse( string ); - return new Date( - accessor.getLong( ChronoField.INSTANT_SECONDS ) * 1000L - + accessor.get( ChronoField.NANO_OF_SECOND ) / 1_000_000 - ); - } - catch ( DateTimeParseException pe) { - throw new HibernateException( "could not parse timestamp string" + string, pe ); - } + return switch ( precision ) { + case TIMESTAMP -> JdbcTimestampJavaType.INSTANCE.fromString( string ); + case DATE -> JdbcDateJavaType.INSTANCE.fromString( string ); + case TIME -> JdbcTimeJavaType.INSTANCE.fromString( string ); + }; +// try { +// final var accessor = JdbcTimestampJavaType.LITERAL_FORMATTER.parse( string ); +// return new Date( +// accessor.getLong( ChronoField.INSTANT_SECONDS ) * 1000L +// + accessor.get( ChronoField.NANO_OF_SECOND ) / 1_000_000 +// ); +// } +// catch ( DateTimeParseException pe) { +// throw new HibernateException( "could not parse timestamp string" + string, pe ); +// } } @Override @@ -108,78 +156,67 @@ public boolean areEqual(Date one, Date another) { if ( one == another) { return true; } - return !( one == null || another == null ) && one.getTime() == another.getTime(); - + return one != null && another != null + && switch ( precision ) { + case DATE -> JdbcDateJavaType.INSTANCE.areEqual( wrapSqlDate( one ), wrapSqlDate( another ) ); + case TIME -> JdbcTimeJavaType.INSTANCE.areEqual( wrapSqlTime( one ), wrapSqlTime( another ) ); + case TIMESTAMP -> + one instanceof Timestamp timestamp && another instanceof Timestamp anotherTimestamp + ? JdbcTimestampJavaType.INSTANCE.areEqual( timestamp, anotherTimestamp ) + : one.getTime() == another.getTime(); + }; } @Override public int extractHashCode(Date value) { - Calendar calendar = Calendar.getInstance(); + var calendar = Calendar.getInstance(); calendar.setTime( value ); - return CalendarJavaType.INSTANCE.extractHashCode( calendar ); + int hashCode = 1; + if ( precision == TemporalType.TIMESTAMP ) { + hashCode = 31 * hashCode + calendar.get(Calendar.MILLISECOND); + } + if ( precision != TemporalType.DATE ) { + hashCode = 31 * hashCode + calendar.get(Calendar.SECOND); + hashCode = 31 * hashCode + calendar.get(Calendar.MINUTE); + hashCode = 31 * hashCode + calendar.get(Calendar.HOUR_OF_DAY); + } + if ( precision != TemporalType.TIME ) { + hashCode = 31 * hashCode + calendar.get(Calendar.DAY_OF_MONTH); + hashCode = 31 * hashCode + calendar.get(Calendar.MONTH); + hashCode = 31 * hashCode + calendar.get(Calendar.YEAR); + } + return hashCode; } @Override public X unwrap(Date value, Class type, WrapperOptions options) { - if ( value == null ) { - return null; - } - if ( java.sql.Date.class.isAssignableFrom( type ) ) { - final java.sql.Date rtn = value instanceof java.sql.Date - ? ( java.sql.Date ) value - : new java.sql.Date( value.getTime() ); - return type.cast( rtn ); - } - if ( java.sql.Time.class.isAssignableFrom( type ) ) { - final java.sql.Time rtn = value instanceof java.sql.Time - ? ( java.sql.Time ) value - : new java.sql.Time( value.getTime() % 86_400_000 ); - return type.cast( rtn ); - } - if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { - final java.sql.Timestamp rtn = value instanceof Timestamp - ? ( java.sql.Timestamp ) value - : new java.sql.Timestamp( value.getTime() ); - return type.cast( rtn ); - } - if ( Date.class.isAssignableFrom( type ) ) { - return type.cast( value ); - } - if ( Calendar.class.isAssignableFrom( type ) ) { - final GregorianCalendar cal = new GregorianCalendar(); - cal.setTimeInMillis( value.getTime() ); - return type.cast( cal ); - } - if ( Long.class.isAssignableFrom( type ) ) { - return type.cast( value.getTime() ); - } - throw unknownUnwrap( type ); + return switch ( precision ) { + case TIMESTAMP -> JdbcTimestampJavaType.INSTANCE.unwrap( wrapSqlTimestamp( value ), type, options ); + case DATE -> JdbcDateJavaType.INSTANCE.unwrap( wrapSqlDate( value ), type, options ); + case TIME -> JdbcTimeJavaType.INSTANCE.unwrap( wrapSqlTime( value ), type, options ); + }; } + @Override public Date wrap(X value, WrapperOptions options) { - if ( value == null ) { - return null; - } - if (value instanceof Date date) { - return date; - } - - if (value instanceof Long longValue) { - return new Date( longValue ); - } - - if (value instanceof Calendar calendar) { - return new Date( calendar.getTimeInMillis() ); - } + return switch ( precision ) { + case TIMESTAMP -> JdbcTimestampJavaType.INSTANCE.wrap( value, options ); + case DATE -> JdbcDateJavaType.INSTANCE.wrap( value, options ); + case TIME -> JdbcTimeJavaType.INSTANCE.wrap( value, options ); + }; + } - throw unknownWrap( value.getClass() ); + @Override + public Object coerce(Object value) { + return wrap( value, null ); } @Override public boolean isWider(JavaType javaType) { - return switch ( javaType.getTypeName() ) { - case "java.sql.Date", "java.sql.Timestamp", "java.util.Calendar" -> true; - default -> false; + return switch ( precision ) { + case TIMESTAMP -> JdbcTimestampJavaType.INSTANCE.isWider( javaType ); + case DATE -> JdbcDateJavaType.INSTANCE.isWider( javaType ); + case TIME -> JdbcTimeJavaType.INSTANCE.isWider( javaType ); }; } @@ -199,4 +236,24 @@ public Date seed( Integer precision, Integer scale, SharedSessionContractImplementor session) { return Timestamp.from( ClockHelper.forPrecision( precision, session ).instant() ); } + + static Timestamp wrapSqlTimestamp(Date date) { + return date instanceof Timestamp timestamp ? timestamp : new Timestamp( date.getTime() ); + } + + static Time wrapSqlTime(Date date) { + return date instanceof Time time ? time : new Time( date.getTime() % 86_400_000 ); + } + + static java.sql.Date wrapSqlDate(java.util.Date value) { + if ( value instanceof java.sql.Date date ) { + final long millis = date.getTime(); + final long dateEpoch = toDateEpoch( millis ); + return dateEpoch == millis ? date : new java.sql.Date( dateEpoch ); + } + else { + return new java.sql.Date( toDateEpoch( value ) ); + } + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java index 9258833af50a..d74bff5d8a6f 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java @@ -12,7 +12,7 @@ import java.time.format.DateTimeParseException; import java.time.temporal.TemporalAccessor; import java.util.Calendar; -import java.util.Date; +import java.sql.Date; import java.util.GregorianCalendar; import org.hibernate.HibernateException; @@ -62,15 +62,13 @@ public TemporalType getPrecision() { @Override public Class getJavaType() { - // wrong, but needed for backward compatibility - //noinspection unchecked, rawtypes - return (Class) java.sql.Date.class; + return java.sql.Date.class; } @Override public boolean isInstance(Object value) { // this check holds true for java.sql.Date as well - return value instanceof Date + return value instanceof java.util.Date && !( value instanceof java.sql.Time ); } @@ -130,7 +128,7 @@ public X unwrap(Date value, Class type, WrapperOptions options) { } if ( java.sql.Date.class.isAssignableFrom( type ) ) { - return type.cast( unwrapSqlDate( value ) ); + return type.cast( value ); } if ( java.util.Date.class.isAssignableFrom( type ) ) { @@ -138,7 +136,7 @@ public X unwrap(Date value, Class type, WrapperOptions options) { } if ( Long.class.isAssignableFrom( type ) ) { - return type.cast( unwrapDateEpoch( value ) ); + return type.cast( toDateEpoch( value ) ); } if ( String.class.isAssignableFrom( type ) ) { @@ -147,49 +145,25 @@ public X unwrap(Date value, Class type, WrapperOptions options) { if ( Calendar.class.isAssignableFrom( type ) ) { final var gregorianCalendar = new GregorianCalendar(); - gregorianCalendar.setTimeInMillis( unwrapDateEpoch( value ) ); + gregorianCalendar.setTimeInMillis( toDateEpoch( value ) ); return type.cast( gregorianCalendar ); } if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) { - return type.cast( new java.sql.Timestamp( unwrapDateEpoch( value ) ) ); + return type.cast( new java.sql.Timestamp( toDateEpoch( value ) ) ); } if ( java.sql.Time.class.isAssignableFrom( type ) ) { - throw new IllegalArgumentException( "Illegal attempt to treat `java.sql.Date` as `java.sql.Time`" ); + throw new IllegalArgumentException( "Illegal attempt to treat 'java.sql.Date' as 'java.sql.Time'" ); } throw unknownUnwrap( type ); } - private LocalDate unwrapLocalDate(Date value) { + private LocalDate unwrapLocalDate(java.util.Date value) { return value instanceof java.sql.Date date ? date.toLocalDate() - : new java.sql.Date( unwrapDateEpoch( value ) ).toLocalDate(); - } - - private java.sql.Date unwrapSqlDate(Date value) { - if ( value instanceof java.sql.Date date ) { - final long dateEpoch = toDateEpoch( date.getTime() ); - return dateEpoch == date.getTime() ? date : new java.sql.Date( dateEpoch ); - } - else { - return new java.sql.Date( unwrapDateEpoch( value ) ); - } - } - - private static long unwrapDateEpoch(Date value) { - return toDateEpoch( value.getTime() ); - } - - private static long toDateEpoch(long value) { - final var calendar = Calendar.getInstance(); - calendar.setTimeInMillis( value ); - calendar.set(Calendar.HOUR_OF_DAY, 0); - calendar.clear(Calendar.MINUTE); - calendar.clear(Calendar.SECOND); - calendar.clear(Calendar.MILLISECOND); - return calendar.getTimeInMillis(); + : new java.sql.Date( toDateEpoch( value ) ).toLocalDate(); } @Override @@ -210,8 +184,8 @@ public Date wrap(Object value, WrapperOptions options) { return new java.sql.Date( toDateEpoch( calendar.getTimeInMillis() ) ); } - if ( value instanceof Date date ) { - return unwrapSqlDate( date ); + if ( value instanceof java.util.Date date ) { + return wrapSqlDate( date ); } if ( value instanceof LocalDate localDate ) { @@ -221,7 +195,32 @@ public Date wrap(Object value, WrapperOptions options) { throw unknownWrap( value.getClass() ); } - private static TemporalAccessor fromDate(Date value) { + static java.sql.Date wrapSqlDate(java.util.Date value) { + if ( value instanceof java.sql.Date date ) { + final long millis = date.getTime(); + final long dateEpoch = toDateEpoch( millis ); + return dateEpoch == millis ? date : new java.sql.Date( dateEpoch ); + } + else { + return new java.sql.Date( toDateEpoch( value ) ); + } + } + + static long toDateEpoch(java.util.Date value) { + return toDateEpoch( value.getTime() ); + } + + static long toDateEpoch(long value) { + final var calendar = Calendar.getInstance(); + calendar.setTimeInMillis( value ); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.clear(Calendar.MINUTE); + calendar.clear(Calendar.SECOND); + calendar.clear(Calendar.MILLISECOND); + return calendar.getTimeInMillis(); + } + + private static TemporalAccessor fromDate(java.util.Date value) { return value instanceof java.sql.Date date ? date.toLocalDate() : LocalDate.ofInstant( value.toInstant(), ZoneOffset.systemDefault() ); @@ -275,9 +274,7 @@ public static class DateMutabilityPlan extends MutableMutabilityPlan { @Override public Date deepCopyNotNull(Date value) { - return value instanceof java.sql.Date - ? value - : new java.sql.Date( value.getTime() ); + return new java.sql.Date( value.getTime() ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java index af0af15fe7f0..b94a20f2c845 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java @@ -37,7 +37,7 @@ * to {@link Time} values. This capability is shared with * {@link JdbcDateJavaType} and {@link JdbcTimestampJavaType}. */ -public class JdbcTimeJavaType extends AbstractTemporalJavaType { +public class JdbcTimeJavaType extends AbstractTemporalJavaType