diff --git a/documentation/src/main/asciidoc/topical/registries/ServiceRegistries.adoc b/documentation/src/main/asciidoc/topical/registries/ServiceRegistries.adoc index 4f91a3274636..42150f386edc 100644 --- a/documentation/src/main/asciidoc/topical/registries/ServiceRegistries.adoc +++ b/documentation/src/main/asciidoc/topical/registries/ServiceRegistries.adoc @@ -28,7 +28,7 @@ service is defined by the interface (service role) +org.hibernate.engine.jdbc.co which declares methods for obtaining and releasing the Connections. There are then multiple implementations of that service contract, varying in how they actually manage the Connections: -* +org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl+ for using a +javax.sql.DataSource+ +* +org.hibernate.engine.jdbc.connections.internal.DataSourceConnectionProvider+ for using a +javax.sql.DataSource+ * +org.hibernate.c3p0.internal.C3P0ConnectionProvider+ for using a C3P0 Connection pool * etc. 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 80dc0affc598..25ff2c9fcea8 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 @@ -75,7 +75,6 @@ import org.hibernate.type.descriptor.jdbc.BlobJdbcType; import org.hibernate.type.descriptor.jdbc.ClobJdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType; -import org.hibernate.type.descriptor.jdbc.NClobJdbcType; import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl; @@ -421,7 +420,7 @@ protected void contributeCockroachTypes(TypeContributions typeContributions, Ser // Force Blob binding to byte[] for CockroachDB jdbcTypeRegistry.addDescriptor( Types.BLOB, BlobJdbcType.MATERIALIZED ); jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.MATERIALIZED ); - jdbcTypeRegistry.addDescriptor( Types.NCLOB, NClobJdbcType.MATERIALIZED ); + jdbcTypeRegistry.addDescriptor( Types.NCLOB, ClobJdbcType.MATERIALIZED ); // The next two contributions are the same as for Postgresql typeContributions.contributeJdbcType( ObjectNullAsBinaryTypeJdbcType.INSTANCE ); 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 48a6b5187487..4879626636a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java @@ -4,27 +4,20 @@ */ package org.hibernate.boot; -import java.io.File; -import java.io.InputStream; -import java.io.Serializable; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; - import org.hibernate.HibernateException; import org.hibernate.Internal; import org.hibernate.boot.archive.spi.InputStreamAccess; import org.hibernate.boot.internal.MetadataBuilderImpl; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmHibernateMapping; -import org.hibernate.boot.jaxb.internal.XmlSources; +import org.hibernate.boot.jaxb.internal.CacheableFileXmlSource; +import org.hibernate.boot.jaxb.internal.FileXmlSource; +import org.hibernate.boot.jaxb.internal.InputStreamAccessXmlSource; +import org.hibernate.boot.jaxb.internal.InputStreamXmlSource; +import org.hibernate.boot.jaxb.internal.JarFileEntryXmlSource; +import org.hibernate.boot.jaxb.internal.UrlXmlSource; import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappingsImpl; import org.hibernate.boot.jaxb.spi.Binding; import org.hibernate.boot.jaxb.spi.JaxbBindableMappingDescriptor; -import org.hibernate.boot.jaxb.spi.XmlSource; import org.hibernate.boot.registry.BootstrapServiceRegistry; import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; import org.hibernate.boot.registry.StandardServiceRegistry; @@ -36,6 +29,17 @@ import org.hibernate.service.ServiceRegistry; import org.hibernate.type.SerializationException; +import java.io.File; +import java.io.InputStream; +import java.io.Serializable; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + import static java.util.Collections.addAll; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; @@ -358,9 +362,8 @@ public MetadataSources addPackage(Package packageRef) { * @return this (for method chaining purposes) */ public MetadataSources addResource(String name) { - final XmlSource xmlSource = XmlSources.fromResource( name, classLoaderService ); final XmlMappingBinderAccess binderAccess = getXmlMappingBinderAccess(); - addXmlBinding( xmlSource.doBind( binderAccess.getMappingBinder() ) ); + addXmlBinding( UrlXmlSource.fromResource( name, classLoaderService, binderAccess.getMappingBinder() ) ); return this; } @@ -388,9 +391,8 @@ public MetadataSources addFile(String path) { * @return this (for method chaining purposes) */ public MetadataSources addFile(File file) { - final XmlSource xmlSource = XmlSources.fromFile( file ); final XmlMappingBinderAccess binderAccess = getXmlMappingBinderAccess(); - addXmlBinding( xmlSource.doBind( binderAccess.getMappingBinder() ) ); + addXmlBinding( FileXmlSource.fromFile( file, binderAccess.getMappingBinder() ) ); return this; } @@ -510,9 +512,13 @@ public MetadataSources addCacheableFile(File file) { * @return this (for method chaining purposes) */ public MetadataSources addCacheableFile(File file, File cacheDirectory) { - final XmlSource xmlSource = XmlSources.fromCacheableFile( file, cacheDirectory ); final XmlMappingBinderAccess binderAccess = getXmlMappingBinderAccess(); - addXmlBinding( xmlSource.doBind( binderAccess.getMappingBinder() ) ); + addXmlBinding( CacheableFileXmlSource.fromCacheableFile( + file, + cacheDirectory, + false, + binderAccess.getMappingBinder() + ) ); return this; } @@ -530,9 +536,13 @@ public MetadataSources addCacheableFile(File file, File cacheDirectory) { * @throws MappingNotFoundException Indicates that the cached file was not found or was not usable. */ public MetadataSources addCacheableFileStrictly(File file) throws SerializationException { - final XmlSource xmlSource = XmlSources.fromCacheableFile( file, true ); final XmlMappingBinderAccess binderAccess = getXmlMappingBinderAccess(); - addXmlBinding( xmlSource.doBind( binderAccess.getMappingBinder() ) ); + addXmlBinding( CacheableFileXmlSource.fromCacheableFile( + file, + null, + true, + binderAccess.getMappingBinder() + ) ); return this; } @@ -550,9 +560,13 @@ public MetadataSources addCacheableFileStrictly(File file) throws SerializationE * @throws MappingNotFoundException Indicates that the cached file was not found or was not usable. */ public MetadataSources addCacheableFileStrictly(File file, File cacheDir) throws SerializationException { - final XmlSource xmlSource = XmlSources.fromCacheableFile( file, cacheDir, true ); final XmlMappingBinderAccess binderAccess = getXmlMappingBinderAccess(); - addXmlBinding( xmlSource.doBind( binderAccess.getMappingBinder() ) ); + addXmlBinding( CacheableFileXmlSource.fromCacheableFile( + file, + cacheDir, + true, + binderAccess.getMappingBinder() + ) ); return this; } @@ -564,9 +578,8 @@ public MetadataSources addCacheableFileStrictly(File file, File cacheDir) throws * @return this (for method chaining purposes) */ public MetadataSources addInputStream(InputStreamAccess xmlInputStreamAccess) { - final XmlSource xmlSource = XmlSources.fromStream( xmlInputStreamAccess ); final XmlMappingBinderAccess binderAccess = getXmlMappingBinderAccess(); - addXmlBinding( xmlSource.doBind( binderAccess.getMappingBinder() ) ); + addXmlBinding( InputStreamAccessXmlSource.fromStreamAccess( xmlInputStreamAccess, binderAccess.getMappingBinder() ) ); return this; } @@ -578,9 +591,8 @@ public MetadataSources addInputStream(InputStreamAccess xmlInputStreamAccess) { * @return this (for method chaining purposes) */ public MetadataSources addInputStream(InputStream xmlInputStream) { - final XmlSource xmlSource = XmlSources.fromStream( xmlInputStream ); final XmlMappingBinderAccess binderAccess = getXmlMappingBinderAccess(); - addXmlBinding( xmlSource.doBind( binderAccess.getMappingBinder() ) ); + addXmlBinding( InputStreamXmlSource.fromStream( xmlInputStream, binderAccess.getMappingBinder() ) ); return this; } @@ -592,9 +604,8 @@ public MetadataSources addInputStream(InputStream xmlInputStream) { * @return this (for method chaining purposes) */ public MetadataSources addURL(URL url) { - final XmlSource xmlSource = XmlSources.fromUrl( url ); final XmlMappingBinderAccess binderAccess = getXmlMappingBinderAccess(); - addXmlBinding( xmlSource.doBind( binderAccess.getMappingBinder() ) ); + addXmlBinding( UrlXmlSource.fromUrl( url, binderAccess.getMappingBinder() ) ); return this; } @@ -610,10 +621,7 @@ public MetadataSources addURL(URL url) { */ public MetadataSources addJar(File jar) { final XmlMappingBinderAccess binderAccess = getXmlMappingBinderAccess(); - XmlSources.fromJar( - jar, - xmlSource -> addXmlBinding( xmlSource.doBind( binderAccess.getMappingBinder() ) ) - ); + JarFileEntryXmlSource.fromJar( jar, binderAccess.getMappingBinder(), this::addXmlBinding ); return this; } 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 004bb79e331a..4ea0e7becce4 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 @@ -166,7 +166,6 @@ import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.mapping.BasicValue; -import org.hibernate.mapping.CheckConstraint; import org.hibernate.mapping.Collection; import org.hibernate.mapping.Column; import org.hibernate.mapping.Component; @@ -227,16 +226,15 @@ public static List> transform( MetadataImplementor bootModel, UnsupportedFeatureHandling unsupportedFeatureHandling) { // perform a first pass over the hbm.xml bindings building much of the transformation-state - final TransformationState transformationState = new TransformationState(); - final List> transformations = - XmlPreprocessor.preprocessHbmXml( hbmXmlBindings, transformationState ); + final var transformationState = new TransformationState(); + final var transformations = XmlPreprocessor.preprocessHbmXml( hbmXmlBindings, transformationState ); // build and perform a pass over the boot model building the rest of the transformation-state BootModelPreprocessor.preprocessBooModel( bootModel, transformationState ); // now we are ready to fully build the mapping.xml transformations for ( int i = 0; i < hbmXmlBindings.size(); i++ ) { - final HbmXmlTransformer hbmXmlTransformer = new HbmXmlTransformer( + final var hbmXmlTransformer = new HbmXmlTransformer( hbmXmlBindings.get( i ), transformations.get( i ), transformationState, @@ -275,8 +273,8 @@ private HbmXmlTransformer( private void performTransformation() { - final JaxbHbmHibernateMapping hbmXmlRoot = hbmXmlBinding.getRoot(); - final JaxbEntityMappingsImpl mappingXmlRoot = mappingXmlBinding.getRoot(); + final var hbmXmlRoot = hbmXmlBinding.getRoot(); + final var mappingXmlRoot = mappingXmlBinding.getRoot(); TransformationHelper.transfer( hbmXmlRoot::getPackage, mappingXmlRoot::setPackage ); TransformationHelper.transfer( hbmXmlRoot::getCatalog, mappingXmlRoot::setCatalog ); @@ -295,53 +293,53 @@ private void performTransformation() { hbmXmlRoot.getClazz().forEach( (hbmEntity) -> { final String entityName = TransformationHelper.determineEntityName( hbmEntity, hbmXmlRoot ); - final JaxbEntityImpl mappingEntity = transformationState.getMappingEntityByName().get( entityName ); - final EntityTypeInfo bootEntityInfo = transformationState.getEntityInfoByName().get( entityName ); + final var mappingEntity = transformationState.getMappingEntityByName().get( entityName ); + final var bootEntityInfo = transformationState.getEntityInfoByName().get( entityName ); assert mappingEntity != null : "Unable to locate JaxbEntityImpl for " + entityName; - assert bootEntityInfo != null : "Unable to locate EntityTypeInfo for " + entityName; + assert bootEntityInfo != null : "Unable to locate EntityTypeInfo for " + entityName; transferRootEntity( hbmEntity, mappingEntity, bootEntityInfo ); } ); hbmXmlRoot.getSubclass().forEach( (hbmSubclass) -> { final String entityName = TransformationHelper.determineEntityName( hbmSubclass, hbmXmlRoot ); - final JaxbEntityImpl mappingEntity = transformationState.getMappingEntityByName().get( entityName ); - final EntityTypeInfo bootEntityInfo = transformationState.getEntityInfoByName().get( entityName ); + final var mappingEntity = transformationState.getMappingEntityByName().get( entityName ); + final var bootEntityInfo = transformationState.getEntityInfoByName().get( entityName ); assert mappingEntity != null : "Unable to locate JaxbEntityImpl for " + entityName; - assert bootEntityInfo != null : "Unable to locate EntityTypeInfo for " + entityName; + assert bootEntityInfo != null : "Unable to locate EntityTypeInfo for " + entityName; transferDiscriminatorSubclass( hbmSubclass, mappingEntity, bootEntityInfo ); final String rootEntityName = bootEntityInfo.getPersistentClass().getRootClass().getEntityName(); - final JaxbEntityImpl rootMappingEntity = transformationState.getMappingEntityByName().get( rootEntityName ); + final var rootMappingEntity = transformationState.getMappingEntityByName().get( rootEntityName ); defineInheritance( rootMappingEntity, InheritanceType.SINGLE_TABLE ); } ); hbmXmlRoot.getJoinedSubclass().forEach( (hbmSubclass) -> { final String entityName = TransformationHelper.determineEntityName( hbmSubclass, hbmXmlRoot ); - final JaxbEntityImpl mappingEntity = transformationState.getMappingEntityByName().get( entityName ); - final EntityTypeInfo bootEntityInfo = transformationState.getEntityInfoByName().get( entityName ); + final var mappingEntity = transformationState.getMappingEntityByName().get( entityName ); + final var bootEntityInfo = transformationState.getEntityInfoByName().get( entityName ); assert mappingEntity != null : "Unable to locate JaxbEntityImpl for " + entityName; - assert bootEntityInfo != null : "Unable to locate EntityTypeInfo for " + entityName; + assert bootEntityInfo != null : "Unable to locate EntityTypeInfo for " + entityName; transferJoinedSubclass( hbmSubclass, mappingEntity, bootEntityInfo ); final String rootEntityName = bootEntityInfo.getPersistentClass().getRootClass().getEntityName(); - final JaxbEntityImpl rootMappingEntity = transformationState.getMappingEntityByName().get( rootEntityName ); + final var rootMappingEntity = transformationState.getMappingEntityByName().get( rootEntityName ); defineInheritance( rootMappingEntity, InheritanceType.JOINED ); } ); hbmXmlRoot.getUnionSubclass().forEach( (hbmSubclass) -> { final String entityName = TransformationHelper.determineEntityName( hbmSubclass, hbmXmlRoot ); - final JaxbEntityImpl mappingEntity = transformationState.getMappingEntityByName().get( entityName ); - final EntityTypeInfo bootEntityInfo = transformationState.getEntityInfoByName().get( entityName ); + final var mappingEntity = transformationState.getMappingEntityByName().get( entityName ); + final var bootEntityInfo = transformationState.getEntityInfoByName().get( entityName ); assert mappingEntity != null : "Unable to locate JaxbEntityImpl for " + entityName; - assert bootEntityInfo != null : "Unable to locate EntityTypeInfo for " + entityName; + assert bootEntityInfo != null : "Unable to locate EntityTypeInfo for " + entityName; transferUnionSubclass( hbmSubclass, mappingEntity, bootEntityInfo ); final String rootEntityName = bootEntityInfo.getPersistentClass().getRootClass().getEntityName(); - final JaxbEntityImpl rootMappingEntity = transformationState.getMappingEntityByName().get( rootEntityName ); + final var rootMappingEntity = transformationState.getMappingEntityByName().get( rootEntityName ); defineInheritance( rootMappingEntity, InheritanceType.TABLE_PER_CLASS ); } ); @@ -352,10 +350,10 @@ private void performTransformation() { private static void dumpTransformed(Origin origin, JaxbEntityMappingsImpl ormRoot) { try { - JAXBContext ctx = JAXBContext.newInstance( JaxbEntityMappingsImpl.class ); - Marshaller marshaller = ctx.createMarshaller(); + var ctx = JAXBContext.newInstance( JaxbEntityMappingsImpl.class ); + var marshaller = ctx.createMarshaller(); marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true ); - final StringWriter stringWriter = new StringWriter(); + final var stringWriter = new StringWriter(); marshaller.marshal( ormRoot, stringWriter ); TRANSFORMATION_LOGGER.debugf( "Transformed hbm.xml (%s):\n%s", origin, stringWriter.toString() ); } @@ -376,8 +374,8 @@ private void transferRootEntity( applyTable( entityInfo.getPersistentClass(), mappingEntity ); - for ( JaxbHbmSynchronizeType hbmSync : hbmClass.getSynchronize() ) { - final JaxbSynchronizedTableImpl sync = new JaxbSynchronizedTableImpl(); + for ( var hbmSync : hbmClass.getSynchronize() ) { + final var sync = new JaxbSynchronizedTableImpl(); sync.setTable( hbmSync.getTable() ); mappingEntity.getSynchronizeTables().add( sync ); } @@ -422,55 +420,57 @@ private void transferRootEntity( transformEntityCaching( hbmClass, mappingEntity ); } - for ( JaxbHbmNamedQueryType hbmQuery : hbmClass.getQuery() ) { - mappingEntity.getNamedQueries().add( transformNamedQuery( hbmQuery, mappingEntity.getName() + "." + hbmQuery.getName() ) ); + for ( var hbmQuery : hbmClass.getQuery() ) { + final String name = mappingEntity.getName() + "." + hbmQuery.getName(); + mappingEntity.getNamedQueries().add( transformNamedQuery( hbmQuery, name ) ); } - for ( JaxbHbmNamedNativeQueryType hbmQuery : hbmClass.getSqlQuery() ) { - mappingEntity.getNamedNativeQueries().add( - transformNamedNativeQuery( hbmQuery, mappingEntity.getName() + "." + hbmQuery.getName() ) - ); + for ( var hbmQuery : hbmClass.getSqlQuery() ) { + final String name = mappingEntity.getName() + "." + hbmQuery.getName(); + mappingEntity.getNamedNativeQueries().add( transformNamedNativeQuery( hbmQuery, name ) ); } - for ( JaxbHbmFilterType hbmFilter : hbmClass.getFilter()) { - mappingEntity.getFilters().add( convert( hbmFilter ) ); + final var filters = mappingEntity.getFilters(); + for ( var hbmFilter : hbmClass.getFilter()) { + filters.add( convert( hbmFilter ) ); } - for ( JaxbHbmFetchProfileType hbmFetchProfile : hbmClass.getFetchProfile() ) { - mappingEntity.getFetchProfiles().add( transferFetchProfile( hbmFetchProfile ) ); + final var fetchProfiles = mappingEntity.getFetchProfiles(); + for ( var hbmFetchProfile : hbmClass.getFetchProfile() ) { + fetchProfiles.add( transferFetchProfile( hbmFetchProfile ) ); } - for ( JaxbHbmDiscriminatorSubclassEntityType hbmSubclass : hbmClass.getSubclass() ) { + for ( var hbmSubclass : hbmClass.getSubclass() ) { final String subclassEntityName = TransformationHelper.determineEntityName( hbmSubclass, hbmXmlBinding.getRoot() ); - final JaxbEntityImpl mappingSubclassEntity = transformationState.getMappingEntityByName().get( subclassEntityName ); - final EntityTypeInfo subclassEntityInfo = transformationState.getEntityInfoByName().get( subclassEntityName ); + final var mappingSubclassEntity = transformationState.getMappingEntityByName().get( subclassEntityName ); + final var subclassEntityInfo = transformationState.getEntityInfoByName().get( subclassEntityName ); transferDiscriminatorSubclass( hbmSubclass, mappingSubclassEntity, subclassEntityInfo ); defineInheritance( mappingEntity, InheritanceType.SINGLE_TABLE ); } - for ( JaxbHbmJoinedSubclassEntityType hbmSubclass : hbmClass.getJoinedSubclass() ) { + for ( var hbmSubclass : hbmClass.getJoinedSubclass() ) { final String subclassEntityName = TransformationHelper.determineEntityName( hbmSubclass, hbmXmlBinding.getRoot() ); - final JaxbEntityImpl mappingSubclassEntity = transformationState.getMappingEntityByName().get( subclassEntityName ); - final EntityTypeInfo subclassEntityInfo = transformationState.getEntityInfoByName().get( subclassEntityName ); + final var mappingSubclassEntity = transformationState.getMappingEntityByName().get( subclassEntityName ); + final var subclassEntityInfo = transformationState.getEntityInfoByName().get( subclassEntityName ); transferJoinedSubclass( hbmSubclass, mappingSubclassEntity, subclassEntityInfo ); defineInheritance( mappingEntity, InheritanceType.TABLE_PER_CLASS ); } - for (JaxbHbmUnionSubclassEntityType hbmSubclass : hbmClass.getUnionSubclass() ) { + for ( var hbmSubclass : hbmClass.getUnionSubclass() ) { final String subclassEntityName = TransformationHelper.determineEntityName( hbmSubclass, hbmXmlBinding.getRoot() ); - final JaxbEntityImpl mappingSubclassEntity = transformationState.getMappingEntityByName().get( subclassEntityName ); - final EntityTypeInfo subclassEntityInfo = transformationState.getEntityInfoByName().get( subclassEntityName ); + final var mappingSubclassEntity = transformationState.getMappingEntityByName().get( subclassEntityName ); + final var subclassEntityInfo = transformationState.getEntityInfoByName().get( subclassEntityName ); transferUnionSubclass( hbmSubclass, mappingSubclassEntity, subclassEntityInfo ); defineInheritance( mappingEntity, InheritanceType.JOINED ); } - for ( JaxbHbmNamedQueryType hbmQuery : hbmClass.getQuery() ) { + for ( var hbmQuery : hbmClass.getQuery() ) { // Tests implied this was the case... final String name = hbmClass.getName() + "." + hbmQuery.getName(); mappingXmlBinding.getRoot().getNamedQueries().add( transformNamedQuery( hbmQuery, name ) ); } - for ( JaxbHbmNamedNativeQueryType hbmQuery : hbmClass.getSqlQuery() ) { + for ( var hbmQuery : hbmClass.getSqlQuery() ) { // Tests implied this was the case... final String name = hbmClass.getName() + "." + hbmQuery.getName(); mappingXmlBinding.getRoot().getNamedNativeQueries().add( transformNamedNativeQuery( hbmQuery, name ) ); @@ -503,10 +503,10 @@ private void transferDiscriminatorSubclass( transferBaseEntityAttributes( hbmSubclass, subclassEntity, subclassEntityInfo ); if ( !hbmSubclass.getSubclass().isEmpty() ) { - for ( JaxbHbmDiscriminatorSubclassEntityType nestedHbmSubclass : hbmSubclass.getSubclass() ) { + for ( var nestedHbmSubclass : hbmSubclass.getSubclass() ) { final String nestedSubclassEntityName = TransformationHelper.determineEntityName( nestedHbmSubclass, hbmXmlBinding.getRoot() ); - final JaxbEntityImpl nestedSubclassSubclassEntity = transformationState.getMappingEntityByName().get( nestedSubclassEntityName ); - final EntityTypeInfo nestedSubclassInfo = transformationState.getEntityInfoByName().get( nestedSubclassEntityName ); + final var nestedSubclassSubclassEntity = transformationState.getMappingEntityByName().get( nestedSubclassEntityName ); + final var nestedSubclassInfo = transformationState.getEntityInfoByName().get( nestedSubclassEntityName ); transferDiscriminatorSubclass( nestedHbmSubclass, nestedSubclassSubclassEntity, nestedSubclassInfo ); } } @@ -523,9 +523,9 @@ private void transferJoinedSubclass( applyTable( subclassEntityInfo.getPersistentClass(), subclassEntity ); - final JaxbHbmKeyType key = hbmSubclass.getKey(); + final var key = hbmSubclass.getKey(); if ( key != null ) { - final JaxbPrimaryKeyJoinColumnImpl joinColumn = new JaxbPrimaryKeyJoinColumnImpl(); + final var joinColumn = new JaxbPrimaryKeyJoinColumnImpl(); // todo (7.0) : formula and multiple columns joinColumn.setName( key.getColumnAttribute() ); subclassEntity.getPrimaryKeyJoinColumns().add( joinColumn ); @@ -533,10 +533,10 @@ private void transferJoinedSubclass( } if ( !hbmSubclass.getJoinedSubclass().isEmpty() ) { - for ( JaxbHbmJoinedSubclassEntityType nestedHbmSubclass : hbmSubclass.getJoinedSubclass() ) { + for ( var nestedHbmSubclass : hbmSubclass.getJoinedSubclass() ) { final String nestedSubclassEntityName = TransformationHelper.determineEntityName( nestedHbmSubclass, hbmXmlBinding.getRoot() ); - final JaxbEntityImpl nestedSubclassSubclassEntity = transformationState.getMappingEntityByName().get( nestedSubclassEntityName ); - final EntityTypeInfo nestedSubclassInfo = transformationState.getEntityInfoByName().get( nestedSubclassEntityName ); + final var nestedSubclassSubclassEntity = transformationState.getMappingEntityByName().get( nestedSubclassEntityName ); + final var nestedSubclassInfo = transformationState.getEntityInfoByName().get( nestedSubclassEntityName ); transferJoinedSubclass( nestedHbmSubclass, nestedSubclassSubclassEntity, nestedSubclassInfo ); } } @@ -555,10 +555,10 @@ private void transferUnionSubclass( applyTable( subclassEntityInfo.getPersistentClass(), subclassEntity ); if ( !hbmSubclass.getUnionSubclass().isEmpty() ) { - for ( JaxbHbmUnionSubclassEntityType nestedHbmSubclass : hbmSubclass.getUnionSubclass() ) { + for ( var nestedHbmSubclass : hbmSubclass.getUnionSubclass() ) { final String nestedSubclassEntityName = TransformationHelper.determineEntityName( nestedHbmSubclass, hbmXmlBinding.getRoot() ); - final JaxbEntityImpl nestedSubclassSubclassEntity = transformationState.getMappingEntityByName().get( nestedSubclassEntityName ); - final EntityTypeInfo nestedSubclassInfo = transformationState.getEntityInfoByName().get( nestedSubclassEntityName ); + final var nestedSubclassSubclassEntity = transformationState.getMappingEntityByName().get( nestedSubclassEntityName ); + final var nestedSubclassInfo = transformationState.getEntityInfoByName().get( nestedSubclassEntityName ); transferUnionSubclass( nestedHbmSubclass, nestedSubclassSubclassEntity, nestedSubclassInfo ); } } @@ -570,7 +570,7 @@ private void transferBaseEntityInformation( EntityTypeInfo bootEntityInfo) { mappingEntity.setMetadataComplete( true ); - final PersistentClass persistentClass = bootEntityInfo.getPersistentClass(); + final var persistentClass = bootEntityInfo.getPersistentClass(); if ( persistentClass.getSuperclass() != null ) { mappingEntity.setExtends( persistentClass.getSuperclass().getEntityName() ); } @@ -621,21 +621,19 @@ private void applyBasicTypeMapping( } else if ( type instanceof CustomType ) { if ( isNotEmpty( hbmTypeAttribute ) ) { - final JaxbUserTypeImpl typeNode = interpretBasicType( + jaxbBasicMapping.setType( interpretBasicType( hbmTypeAttribute, null, transformationState.getTypeDefMap().get( hbmTypeAttribute ) - ); - jaxbBasicMapping.setType( typeNode ); + ) ); } if ( hbmType != null ) { - final JaxbUserTypeImpl typeNode = interpretBasicType( + jaxbBasicMapping.setType( interpretBasicType( hbmType.getName(), hbmType, transformationState.getTypeDefMap().get( hbmType.getName() ) - ); - jaxbBasicMapping.setType( typeNode ); + ) ); } } else if ( type instanceof ConvertedBasicType convertedType ) { @@ -659,7 +657,7 @@ else if ( table.isView() ) { jaxbEntity.setTableExpression( table.getViewQuery() ); } else { - final JaxbTableImpl jaxbTable = new JaxbTableImpl(); + final var jaxbTable = new JaxbTableImpl(); jaxbEntity.setTable( jaxbTable ); jaxbTable.setName( table.getName() ); jaxbTable.setComment( table.getComment() ); @@ -671,8 +669,8 @@ private static void transferBaseTableInfo(Table table, JaxbTableMapping jaxbTabl jaxbTableMapping.setCatalog( table.getCatalog() ); jaxbTableMapping.setSchema( table.getSchema() ); - for ( CheckConstraint check : table.getChecks() ) { - final JaxbCheckConstraintImpl jaxbCheckConstraint = new JaxbCheckConstraintImpl(); + for ( var check : table.getChecks() ) { + final var jaxbCheckConstraint = new JaxbCheckConstraintImpl(); jaxbTableMapping.getCheckConstraints().add( jaxbCheckConstraint ); jaxbCheckConstraint.setName( check.getName() ); jaxbCheckConstraint.setConstraint( check.getConstraint() ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/TransformationState.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/TransformationState.java index 94070afb7a7d..4cdc1813bc43 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/TransformationState.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/TransformationState.java @@ -88,11 +88,8 @@ public void registerMappableAttributesByColumns( String entityName, String attributeName, List selectables) { - final Map, String> attributeByColumnsMap = mappableAttributesByColumnsByEntity.computeIfAbsent( - entityName, - s -> new HashMap<>() - ); - attributeByColumnsMap.put( selectables, attributeName ); + mappableAttributesByColumnsByEntity.computeIfAbsent( entityName, s -> new HashMap<>() ) + .put( selectables, attributeName ); } public Map getTypeDefMap() { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/UnknownEntityReferenceException.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/UnknownEntityReferenceException.java deleted file mode 100644 index 76c731d9bf1a..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/UnknownEntityReferenceException.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.boot.jaxb.hbm.transform; - -import java.util.Locale; - -import org.hibernate.MappingException; -import org.hibernate.boot.jaxb.Origin; - -/** - * We encountered a reference to an entity by name which we cannot resolve - * - * @author Steve Ebersole - */ -public class UnknownEntityReferenceException extends MappingException { - public UnknownEntityReferenceException(String name, Origin origin) { - super( String.format( - Locale.ROOT, - "Could not resolve entity name `%s` : %s (%s)", - name, - origin.getName(), - origin.getType() - ) ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/XmlPreprocessor.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/XmlPreprocessor.java index 0123bab7c8ac..82b400616de4 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/XmlPreprocessor.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/XmlPreprocessor.java @@ -16,7 +16,8 @@ import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappingsImpl; import org.hibernate.boot.jaxb.spi.Binding; -import org.hibernate.internal.util.collections.CollectionHelper; + +import static org.hibernate.internal.util.collections.CollectionHelper.arrayList; /** * @author Steve Ebersole @@ -25,7 +26,7 @@ public class XmlPreprocessor { public static List> preprocessHbmXml( List> hbmXmlBindings, TransformationState transformationState) { - final List> mappingBindings = CollectionHelper.arrayList( hbmXmlBindings.size() ); + final List> mappingBindings = arrayList( hbmXmlBindings.size() ); hbmXmlBindings.forEach( (hbmXmlBinding) -> preProcessHbmXml( hbmXmlBinding, mappingBindings, transformationState ) ); return mappingBindings; } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/CacheableFileXmlSource.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/CacheableFileXmlSource.java index 0cfa4a06d2e6..15109c4cb83b 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/CacheableFileXmlSource.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/CacheableFileXmlSource.java @@ -4,85 +4,74 @@ */ package org.hibernate.boot.jaxb.internal; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.Serializable; - import org.hibernate.boot.MappingException; import org.hibernate.boot.jaxb.Origin; import org.hibernate.boot.jaxb.SourceType; -import org.hibernate.boot.jaxb.spi.Binder; import org.hibernate.boot.jaxb.spi.Binding; -import org.hibernate.boot.jaxb.spi.XmlSource; +import org.hibernate.boot.jaxb.spi.JaxbBindableMappingDescriptor; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.type.SerializationException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; + /** + * Support for creating a mapping {@linkplain Binding binding} from "cached" XML files. + *

+ * This is a legacy feature, caching a serialized form of the {@linkplain JaxbBindableMappingDescriptor JAXB model} + * into a file for later use. While not deprecated per se, its use is discouraged. + * + * @see MappingBinder + * * @author Steve Ebersole */ -public class CacheableFileXmlSource extends XmlSource { +public class CacheableFileXmlSource { private static final CoreMessageLogger log = CoreLogging.messageLogger( CacheableFileXmlSource.class ); - private final File xmlFile; - private final File serFile; - private final boolean strict; - - public CacheableFileXmlSource(Origin origin, File xmlFile, File cachedFileDir, boolean strict) { - super( origin ); - this.xmlFile = xmlFile; - this.strict = strict; - - this.serFile = new File( cachedFileDir, xmlFile.getName() + ".bin" ); - - if ( strict ) { - if ( !serFile.exists() ) { - throw new MappingException( - String.format( "Cached file [%s] could not be found", origin.getName() ), - origin - ); - } - if ( isSerfileObsolete() ) { - throw new MappingException( - String.format( "Cached file [%s] could not be used as the mapping file is newer", origin.getName() ), - origin - ); - } - } + public static Binding fromCacheableFile( + File xmlFile, + File serLocation, + boolean strict, + MappingBinder binder) { + final Origin origin = new Origin( SourceType.FILE, xmlFile.getAbsolutePath() ); + return fromCacheableFile( xmlFile, serLocation, origin, strict, binder ); } - public static File determineCachedFile(File xmlFile) { - return new File( xmlFile.getAbsolutePath() + ".bin" ); - } + public static Binding fromCacheableFile( + File xmlFile, + File serLocation, + Origin origin, + boolean strict, + MappingBinder binder) { + final File serFile = resolveSerFile( xmlFile, serLocation ); - @Override - public Binding doBind(Binder binder) { if ( strict ) { try { - return new Binding<>( readSerFile(), getOrigin() ); + return new Binding<>( readSerFile( serFile ), origin ); } catch ( SerializationException e ) { throw new MappingException( - String.format( "Unable to deserialize from cached file [%s]", getOrigin().getName() ) , + String.format( "Unable to deserialize from cached file [%s]", origin.getName() ) , e, - getOrigin() + origin ); } catch ( FileNotFoundException e ) { throw new MappingException( - String.format( "Unable to locate cached file [%s]", getOrigin().getName() ) , + String.format( "Unable to locate cached file [%s]", origin.getName() ) , e, - getOrigin() + origin ); } } else { - if ( !isSerfileObsolete() ) { + if ( !isSerfileObsolete( xmlFile, serFile ) ) { try { - return new Binding<>( readSerFile(), getOrigin() ); + return new Binding<>( readSerFile( serFile ), origin ); } catch ( SerializationException e ) { log.unableToDeserializeCache( serFile.getName(), e ); @@ -96,32 +85,59 @@ public Binding doBind(Binder binder) { } log.readingMappingsFromFile( xmlFile.getPath() ); - final Binding binding = FileXmlSource.doBind( binder, xmlFile, getOrigin() ); + final Binding binding = FileXmlSource.fromFile( xmlFile, binder ); - writeSerFile( binding ); + writeSerFile( binding.getRoot(), xmlFile, serFile ); return binding; } } - private T readSerFile() throws SerializationException, FileNotFoundException { - log.readingCachedMappings( serFile ); - return SerializationHelper.deserialize( new FileInputStream( serFile ) ); + /** + * Determine the ser file for a given mapping XML file. + * + * @param xmlFile The source mapping XML file + * @param serLocation The location details about the ser file. Can be one of 3 things:

    + *
  • {@code null} indicating we should {@linkplain #determineCachedFile(File) calculate} the File reference in the same directory as the {@code xmlFile} + *
  • a {@linkplain File#isDirectory() directory} indicating we should {@linkplain #determineCachedFile(File,File) calculate} the File reference in the given directory + * the {@linkplain File#isFile() file} to use + *
+ * + * @return The ser file reference. + */ + public static File resolveSerFile(File xmlFile, File serLocation) { + if ( serLocation == null ) { + return determineCachedFile( xmlFile ); + } + if ( serLocation.isDirectory() ) { + return determineCachedFile( xmlFile, serLocation ); + } + assert serLocation.isFile(); + return serLocation; } - private void writeSerFile(Object binding) { - writeSerFile( (Serializable) binding, xmlFile, serFile ); + public static File determineCachedFile(File xmlFile) { + return new File( xmlFile.getAbsolutePath() + ".bin" ); } - private static void writeSerFile(Serializable binding, File xmlFile, File serFile) { - if ( binding instanceof Binding bindingWrapper ) { - binding = (Serializable) bindingWrapper.getRoot(); - } + public static File determineCachedFile(File xmlFile, File serDirectory) { + return new File( serDirectory, xmlFile.getName() + ".bin" ); + } + + private static T readSerFile(File serFile) throws SerializationException, FileNotFoundException { + log.readingCachedMappings( serFile ); + return SerializationHelper.deserialize( new FileInputStream( serFile ) ); + } + + private static void writeSerFile( + T jaxbModel, + File xmlFile, + File serFile) { try ( FileOutputStream fos = new FileOutputStream( serFile ) ) { if ( log.isTraceEnabled() ) { log.tracef( "Writing cache file for: %s to: %s", xmlFile.getAbsolutePath(), serFile.getAbsolutePath() ); } - SerializationHelper.serialize( binding, fos ); + SerializationHelper.serialize( jaxbModel, fos ); boolean success = serFile.setLastModified( System.currentTimeMillis() ); if ( !success ) { log.warn( "Could not update cacheable hbm.xml bin file timestamp" ); @@ -132,21 +148,19 @@ private static void writeSerFile(Serializable binding, File xmlFile, File serFil } } - public static void createSerFile(File xmlFile, Binder binder) { + public static void createSerFile(File xmlFile, MappingBinder binder) { createSerFile( xmlFile, determineCachedFile( xmlFile ), binder ); } - public static void createSerFile(File xmlFile, File outputFile, Binder binder) { - final Origin origin = new Origin( SourceType.FILE, xmlFile.getAbsolutePath() ); - writeSerFile( - FileXmlSource.doBind( binder, xmlFile, origin ), - xmlFile, - outputFile - ); + public static void createSerFile(File xmlFile, File outputFile, MappingBinder binder) { + final Binding binding = FileXmlSource.fromFile( xmlFile, binder ); + writeSerFile( binding.getRoot(), xmlFile, outputFile ); } - private boolean isSerfileObsolete() { - return xmlFile.exists() && serFile.exists() && xmlFile.lastModified() > serFile.lastModified(); + public static boolean isSerfileObsolete(File xmlFile, File serFile) { + return xmlFile.exists() + && serFile.exists() + && xmlFile.lastModified() > serFile.lastModified(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/FileXmlSource.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/FileXmlSource.java index 90a04a9ffb66..2a4fc09328c5 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/FileXmlSource.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/FileXmlSource.java @@ -4,33 +4,41 @@ */ package org.hibernate.boot.jaxb.internal; +import org.hibernate.boot.MappingNotFoundException; +import org.hibernate.boot.jaxb.Origin; +import org.hibernate.boot.jaxb.SourceType; +import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.boot.jaxb.spi.JaxbBindableMappingDescriptor; + import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import org.hibernate.boot.MappingNotFoundException; -import org.hibernate.boot.jaxb.Origin; -import org.hibernate.boot.jaxb.spi.Binder; -import org.hibernate.boot.jaxb.spi.Binding; -import org.hibernate.boot.jaxb.spi.XmlSource; +import static org.hibernate.boot.jaxb.JaxbLogger.JAXB_LOGGER; /** + * Support for processing mapping XML from a {@linkplain File} reference. + * + * @see MappingBinder + * * @author Steve Ebersole */ -public class FileXmlSource extends XmlSource { - private final File file; +public class FileXmlSource { + /** + * Create a mapping {@linkplain Binding binding} from a File reference. + */ + public static Binding fromFile( + File file, + MappingBinder mappingBinder) { + final String filePath = file.getPath(); + JAXB_LOGGER.tracef( "Reading mappings from file: %s", filePath ); - public FileXmlSource(Origin origin, File file) { - super( origin ); - this.file = file; - } + final Origin origin = new Origin( SourceType.FILE, filePath ); - @Override - public Binding doBind(Binder binder) { - return doBind( binder, file, getOrigin() ); - } + if ( !file.exists() ) { + throw new MappingNotFoundException( origin ); + } - public static Binding doBind(Binder binder, File file, Origin origin) { final FileInputStream fis; try { fis = new FileInputStream( file ); @@ -38,6 +46,7 @@ public static Binding doBind(Binder binder, File file, Origin origin) catch ( FileNotFoundException e ) { throw new MappingNotFoundException( e, origin ); } - return InputStreamXmlSource.doBind( binder, fis, origin, true ); + + return InputStreamXmlSource.fromStream( fis, origin, true, mappingBinder ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/InputStreamAccessXmlSource.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/InputStreamAccessXmlSource.java index 9d97286e88c4..408044409a1b 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/InputStreamAccessXmlSource.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/InputStreamAccessXmlSource.java @@ -6,29 +6,48 @@ import org.hibernate.boot.archive.spi.InputStreamAccess; import org.hibernate.boot.jaxb.Origin; -import org.hibernate.boot.jaxb.spi.Binder; +import org.hibernate.boot.jaxb.SourceType; import org.hibernate.boot.jaxb.spi.Binding; -import org.hibernate.boot.jaxb.spi.XmlSource; +import org.hibernate.boot.jaxb.spi.JaxbBindableMappingDescriptor; + +import static org.hibernate.boot.jaxb.JaxbLogger.JAXB_LOGGER; /** + * Support for processing mapping XML from a {@linkplain InputStreamAccess} reference. + * * @author Steve Ebersole + * + * @see MappingBinder */ -public class InputStreamAccessXmlSource extends XmlSource { - private final InputStreamAccess inputStreamAccess; - - public InputStreamAccessXmlSource(Origin origin, InputStreamAccess inputStreamAccess) { - super( origin ); - this.inputStreamAccess = inputStreamAccess; +public class InputStreamAccessXmlSource { + /** + * Create a mapping {@linkplain Binding binding} from an input stream. + * + * @apiNote This method does not close the given {@code inputStream}. + */ + public static Binding fromStreamAccess( + InputStreamAccess inputStreamAccess, + MappingBinder mappingBinder) { + return fromStreamAccess( + inputStreamAccess, + new Origin( SourceType.INPUT_STREAM, inputStreamAccess.getStreamName() ), + mappingBinder + ); } - @Override - public Binding doBind(Binder binder) { - return doBind( binder, inputStreamAccess, getOrigin() ); - } + /** + * Create a mapping {@linkplain Binding binding} from an input stream. + * + * @apiNote This method does not close the given {@code inputStream}. + */ + public static Binding fromStreamAccess( + InputStreamAccess inputStreamAccess, + Origin origin, + MappingBinder mappingBinder) { + JAXB_LOGGER.trace( "reading mappings from InputStreamAccess" ); - public static Binding doBind(Binder binder, InputStreamAccess inputStreamAccess, Origin origin) { - return inputStreamAccess.fromStream( - inputStream -> binder.bind( inputStream, origin ) + return inputStreamAccess.fromStream( (stream) -> + InputStreamXmlSource.fromStream( stream, origin, false, mappingBinder ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/InputStreamXmlSource.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/InputStreamXmlSource.java index c7ad607f2ec4..cc3a711b1da6 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/InputStreamXmlSource.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/InputStreamXmlSource.java @@ -4,41 +4,54 @@ */ package org.hibernate.boot.jaxb.internal; -import java.io.IOException; -import java.io.InputStream; - import org.hibernate.boot.InvalidMappingException; import org.hibernate.boot.jaxb.Origin; -import org.hibernate.boot.jaxb.spi.Binder; +import org.hibernate.boot.jaxb.SourceType; import org.hibernate.boot.jaxb.spi.Binding; -import org.hibernate.boot.jaxb.spi.XmlSource; -import org.hibernate.internal.CoreLogging; +import org.hibernate.boot.jaxb.spi.JaxbBindableMappingDescriptor; -import org.jboss.logging.Logger; +import java.io.IOException; +import java.io.InputStream; + +import static org.hibernate.boot.jaxb.JaxbLogger.JAXB_LOGGER; /** + * Support for processing mapping XML from a {@linkplain InputStream} reference. + * + * @see MappingBinder + * * @author Steve Ebersole */ -public class InputStreamXmlSource extends XmlSource { - private static final Logger log = CoreLogging.logger( InputStreamXmlSource.class ); - - private final InputStream inputStream; - private final boolean autoClose; - - public InputStreamXmlSource(Origin origin, InputStream inputStream, boolean autoClose) { - super( origin ); - this.inputStream = inputStream; - this.autoClose = autoClose; - } +public class InputStreamXmlSource { + /** + * Create a mapping {@linkplain Binding binding} from an input stream. + * + * @apiNote This method does not close the given {@code inputStream}. + */ + public static Binding fromStream( + InputStream inputStream, + MappingBinder mappingBinder) { + JAXB_LOGGER.trace( "reading mappings from InputStream" ); - @Override - public Binding doBind(Binder binder) { - return doBind( binder, inputStream, getOrigin(), autoClose ); + final Origin origin = new Origin( SourceType.INPUT_STREAM, null ); + return fromStream( inputStream, origin, false, mappingBinder ); } - public static Binding doBind(Binder binder, InputStream inputStream, Origin origin, boolean autoClose) { + /** + * Utility form to create a {@linkplain Binding binding} from an input source. + * + * @param stream The stream from which to read the mappings + * @param origin Description of the source from which the stream came + * @param autoClose Whether to {@linkplain InputStream#close() close} the stream after we have processed it + * @param binder The JAXB binder to use + */ + public static Binding fromStream( + InputStream stream, + Origin origin, + boolean autoClose, + MappingBinder binder) { try { - return binder.bind( inputStream, origin ); + return binder.bind( stream, origin ); } catch ( Exception e ) { throw new InvalidMappingException( origin, e ); @@ -46,10 +59,10 @@ public static Binding doBind(Binder binder, InputStream inputStream, O finally { if ( autoClose ) { try { - inputStream.close(); + stream.close(); } catch ( IOException ignore ) { - log.trace( "Was unable to close input stream" ); + JAXB_LOGGER.trace( "Was unable to close input stream" ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/JarFileEntryXmlSource.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/JarFileEntryXmlSource.java index 34c02faf378f..47e0140dc870 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/JarFileEntryXmlSource.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/JarFileEntryXmlSource.java @@ -4,35 +4,72 @@ */ package org.hibernate.boot.jaxb.internal; +import org.hibernate.boot.MappingException; +import org.hibernate.boot.MappingNotFoundException; +import org.hibernate.boot.jaxb.Origin; +import org.hibernate.boot.jaxb.SourceType; +import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.boot.jaxb.spi.JaxbBindableMappingDescriptor; + +import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.Enumeration; +import java.util.function.Consumer; +import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.zip.ZipEntry; -import org.hibernate.boot.MappingException; -import org.hibernate.boot.jaxb.Origin; -import org.hibernate.boot.jaxb.spi.Binder; -import org.hibernate.boot.jaxb.spi.Binding; -import org.hibernate.boot.jaxb.spi.XmlSource; +import static org.hibernate.boot.jaxb.JaxbLogger.JAXB_LOGGER; /** + * Support for creating a mapping {@linkplain Binding binding} from a JAR file entry. + * + * @see MappingBinder + * * @author Steve Ebersole */ -public class JarFileEntryXmlSource extends XmlSource { - private final JarFile jarFile; - private final ZipEntry jarFileEntry; +public class JarFileEntryXmlSource { - public JarFileEntryXmlSource( - Origin origin, - JarFile jarFile, - ZipEntry jarFileEntry) { - super( origin ); - this.jarFile = jarFile; - this.jarFileEntry = jarFileEntry; + /** + * Create a mapping {@linkplain Binding binding} for each entry in a JAR file that + * is a {@code hbm.xml} mapping file. This binding is then handed back to the given + * consumer. + * + * @apiNote Assumes that any file named {@code *.hbm.xml} is a mapping document. + * Does not support {@code orm.xml} files. + */ + public static void fromJar( + File jar, + MappingBinder mappingBinder, + Consumer> consumer) { + JAXB_LOGGER.tracef( "Seeking mapping documents in jar file: %s", jar.getName() ); + + final Origin origin = new Origin( SourceType.JAR, jar.getAbsolutePath() ); + + try ( JarFile jarFile = new JarFile(jar) ) { + final Enumeration entries = jarFile.entries(); + while ( entries.hasMoreElements() ) { + final JarEntry jarEntry = entries.nextElement(); + if ( jarEntry.getName().endsWith(".hbm.xml") ) { + JAXB_LOGGER.tracef( "Found 'hbm.xml' mapping in jar: %s", jarEntry.getName() ); + consumer.accept( fromJarEntry( jarFile, jarEntry, origin, mappingBinder ) ); + } + } + } + catch ( IOException e ) { + throw new MappingNotFoundException( e, origin ); + } } - @Override - public Binding doBind(Binder binder) { + /** + * Create a mapping {@linkplain Binding binding} from a JAR file entry. + */ + public static Binding fromJarEntry( + JarFile jarFile, + ZipEntry jarFileEntry, + Origin origin, + MappingBinder mappingBinder) { final InputStream stream; try { stream = jarFile.getInputStream( jarFileEntry ); @@ -45,10 +82,10 @@ public Binding doBind(Binder binder) { jarFileEntry.getName() ), e, - getOrigin() + origin ); } - return InputStreamXmlSource.doBind( binder, stream, getOrigin(), true ); + return InputStreamXmlSource.fromStream( stream, origin, true, mappingBinder ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/JaxpSourceXmlSource.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/JaxpSourceXmlSource.java deleted file mode 100644 index bb42a14ee221..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/JaxpSourceXmlSource.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.boot.jaxb.internal; - -import javax.xml.transform.Source; - -import org.hibernate.boot.jaxb.Origin; -import org.hibernate.boot.jaxb.spi.Binder; -import org.hibernate.boot.jaxb.spi.Binding; -import org.hibernate.boot.jaxb.spi.XmlSource; - -/** - * @author Steve Ebersole - */ -public class JaxpSourceXmlSource extends XmlSource { - private final Source jaxpSource; - - public JaxpSourceXmlSource(Origin origin, Source jaxpSource) { - super( origin ); - this.jaxpSource = jaxpSource; - } - - @Override - public Binding doBind(Binder binder) { - return binder.bind( jaxpSource, getOrigin() ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/UrlXmlSource.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/UrlXmlSource.java index 97345f87fcf0..cdfb97aefc28 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/UrlXmlSource.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/UrlXmlSource.java @@ -4,41 +4,83 @@ */ package org.hibernate.boot.jaxb.internal; +import org.hibernate.boot.MappingException; +import org.hibernate.boot.MappingNotFoundException; +import org.hibernate.boot.jaxb.Origin; +import org.hibernate.boot.jaxb.SourceType; +import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.boot.jaxb.spi.JaxbBindableMappingDescriptor; +import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; + import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.UnknownHostException; -import org.hibernate.boot.MappingException; -import org.hibernate.boot.MappingNotFoundException; -import org.hibernate.boot.jaxb.Origin; -import org.hibernate.boot.jaxb.spi.Binder; -import org.hibernate.boot.jaxb.spi.Binding; -import org.hibernate.boot.jaxb.spi.XmlSource; +import static org.hibernate.boot.jaxb.JaxbLogger.JAXB_LOGGER; /** + * Support for processing mapping XML from a {@linkplain URL} reference. + * + * @see MappingBinder + * * @author Steve Ebersole */ -public class UrlXmlSource extends XmlSource { +public class UrlXmlSource { + /** + * Create a mapping {@linkplain Binding binding} from a classpath resource (via URL). + * + * @see #fromUrl(URL, Origin, MappingBinder) + */ + public static Binding fromResource( + String resourceName, + ClassLoaderService classLoaderService, + MappingBinder mappingBinder) { + JAXB_LOGGER.tracef( "Reading mappings from resource: %s", resourceName ); + + final Origin origin = new Origin( SourceType.RESOURCE, resourceName ); + final URL url = classLoaderService.locateResource( resourceName ); + if ( url == null ) { + throw new MappingNotFoundException( origin ); + } - private final URL url; + return fromUrl( url, origin, mappingBinder ); + } - public UrlXmlSource(Origin origin, URL url) { - super( origin ); - this.url = url; + /** + * Create a mapping {@linkplain Binding binding} from a URL + * + * @see #fromUrl(URL, Origin, MappingBinder) + */ + public static Binding fromUrl( + URL url, + MappingBinder mappingBinder) { + final Origin origin = new Origin( SourceType.URL, url.toExternalForm() ); + return fromUrl( url, origin, mappingBinder ); } - @Override - public Binding doBind(Binder binder) { + /** + * Create a mapping {@linkplain Binding binding} from a URL + * + * @param url The url from which to read the mapping information + * @param origin Description of the source from which the url came + * @param binder The JAXB binder to use + */ + public static Binding fromUrl( + URL url, + Origin origin, + MappingBinder binder) { + JAXB_LOGGER.tracef( "Reading mapping document from URL: %s", origin.getName() ); + try { InputStream stream = url.openStream(); - return InputStreamXmlSource.doBind( binder, stream, getOrigin(), true ); + return InputStreamXmlSource.fromStream( stream, origin, true, binder ); } catch (UnknownHostException e) { - throw new MappingNotFoundException( "Invalid URL", e, getOrigin() ); + throw new MappingNotFoundException( "Invalid URL", e, origin ); } catch (IOException e) { - throw new MappingException( "Unable to open URL InputStream", e, getOrigin() ); + throw new MappingException( "Unable to open URL InputStream", e, origin ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/XmlSources.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/XmlSources.java deleted file mode 100644 index fdc653f33fdd..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/XmlSources.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.boot.jaxb.internal; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Enumeration; -import java.util.function.Consumer; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import javax.xml.transform.dom.DOMSource; - -import org.hibernate.boot.MappingNotFoundException; -import org.hibernate.boot.archive.spi.InputStreamAccess; -import org.hibernate.boot.jaxb.Origin; -import org.hibernate.boot.jaxb.SourceType; -import org.hibernate.boot.jaxb.spi.XmlSource; -import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; - -import org.w3c.dom.Document; - -import static org.hibernate.boot.jaxb.JaxbLogger.JAXB_LOGGER; - -/** - * Helper for building and handling {@link XmlSource} references. - *

- * An {@code XmlSource} represents an XML document containing - * O/R mapping metadata, either a JPA {@code orm.xml} file, or a - * Hibernate {@code .hbm.xml} file. - * - * @author Steve Ebersole - */ -public class XmlSources { - /** - * Create an {@link XmlSource} from a named resource - */ - public static XmlSource fromResource(String resourceName, ClassLoaderService classLoaderService) { - JAXB_LOGGER.tracef( "Reading mappings from resource: %s", resourceName ); - - final Origin origin = new Origin( SourceType.RESOURCE, resourceName ); - final URL url = classLoaderService.locateResource( resourceName ); - if ( url == null ) { - throw new MappingNotFoundException( origin ); - } - - return new UrlXmlSource( origin, url ); - } - - /** - * Create an {@link XmlSource} from a URL - */ - public static XmlSource fromUrl(URL url) { - final String urlExternalForm = url.toExternalForm(); - JAXB_LOGGER.tracef( "Reading mapping document from URL: %s", urlExternalForm ); - - final Origin origin = new Origin( SourceType.URL, urlExternalForm ); - return new UrlXmlSource( origin, url ); - } - - public static XmlSource fromFile(File file) { - final String filePath = file.getPath(); - JAXB_LOGGER.tracef( "Reading mappings from file: %s", filePath ); - - final Origin origin = new Origin( SourceType.FILE, filePath ); - - if ( !file.exists() ) { - throw new MappingNotFoundException( origin ); - } - - return new FileXmlSource( origin, file ); - } - - public static XmlSource fromCacheableFile(File file) { - return fromCacheableFile( file, file.getParentFile() ); - } - - public static XmlSource fromCacheableFile(File file, File cacheableDir) { - return fromCacheableFile( file, cacheableDir, false ); - } - - public static XmlSource fromCacheableFile(File file, boolean strict) { - return fromCacheableFile( file, file.getParentFile(), strict ); - } - - public static XmlSource fromCacheableFile(File file, File cacheableDir, boolean strict) { - final String filePath = file.getPath(); - JAXB_LOGGER.tracef( "Reading mappings from cacheable file: %s", filePath ); - - final Origin origin = new Origin( SourceType.FILE, filePath ); - return new CacheableFileXmlSource( origin, file, cacheableDir, strict ); - } - - public static XmlSource fromStream(InputStreamAccess inputStreamAccess) { - final String streamName = inputStreamAccess.getStreamName(); - JAXB_LOGGER.tracef( "Reading mappings from InputStreamAccess: %s", streamName ); - - final Origin origin = new Origin( SourceType.INPUT_STREAM, streamName ); - return new InputStreamAccessXmlSource( origin, inputStreamAccess ); - } - - public static XmlSource fromStream(InputStream inputStream) { - JAXB_LOGGER.trace( "reading mappings from InputStream" ); - - final Origin origin = new Origin( SourceType.INPUT_STREAM, null ); - return new InputStreamXmlSource( origin, inputStream, false ); - } - - public static XmlSource fromDocument(Document document) { - JAXB_LOGGER.trace( "reading mappings from DOM" ); - final Origin origin = new Origin( SourceType.DOM, Origin.UNKNOWN_FILE_PATH ); - return new JaxpSourceXmlSource( origin, new DOMSource( document ) ); - } - - /** - * Read all {@code .hbm.xml} mappings from a jar file and pass them - * to the given {@link Consumer}. - *

- * Assumes that any file named {@code *.hbm.xml} is a mapping document. - * This method does not support {@code orm.xml} files! - * - * @param jar a jar file - * @param consumer a consumer of the resulting {@linkplain XmlSource XML sources} - */ - public static void fromJar(File jar, Consumer consumer) { - JAXB_LOGGER.tracef( "Seeking mapping documents in jar file: %s", jar.getName() ); - - final Origin origin = new Origin( SourceType.JAR, jar.getAbsolutePath() ); - - try ( JarFile jarFile = new JarFile(jar) ) { - final Enumeration entries = jarFile.entries(); - while ( entries.hasMoreElements() ) { - final JarEntry jarEntry = entries.nextElement(); - if ( jarEntry.getName().endsWith(".hbm.xml") ) { - JAXB_LOGGER.tracef( "Found 'hbm.xml' mapping in jar: %s", jarEntry.getName() ); - consumer.accept( new JarFileEntryXmlSource( origin, jarFile, jarEntry ) ); - } - } - } - catch ( IOException e ) { - throw new MappingNotFoundException( e, origin ); - } - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/spi/JaxbBindableMappingDescriptor.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/spi/JaxbBindableMappingDescriptor.java index 2d74e3c3f272..831a700f35ed 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/spi/JaxbBindableMappingDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/spi/JaxbBindableMappingDescriptor.java @@ -4,6 +4,8 @@ */ package org.hibernate.boot.jaxb.spi; +import java.io.Serializable; + /** * Common type for things that can get be bound to a {@link Binding} for * mapping documents. @@ -13,5 +15,5 @@ * * @author Steve Ebersole */ -public interface JaxbBindableMappingDescriptor { +public interface JaxbBindableMappingDescriptor extends Serializable { } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/spi/XmlSource.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/spi/XmlSource.java deleted file mode 100644 index 799567bcb319..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/spi/XmlSource.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.boot.jaxb.spi; - -import org.hibernate.boot.jaxb.Origin; - -/** - * An XML document containing O/R mapping metadata, either: - *

    - *
  • a JPA {@code orm.xml} file, or - *
  • a Hibernate {@code .hbm.xml} file. - *
- * - * @author Steve Ebersole - */ -public abstract class XmlSource { - private final Origin origin; - - protected XmlSource(Origin origin) { - this.origin = origin; - } - - public Origin getOrigin() { - return origin; - } - - public abstract Binding doBind(Binder binder); -} 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 52a057a8155d..66833859f886 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 @@ -630,6 +630,11 @@ private static PropertyData getUniqueIdPropertyFromBaseClass( return baseClassElements.get( 0 ); } + /** + * Given the id class of the current entity, as specified by an + * {@link IdClass} annotation, determine if it's actually the + * identifier type or {@link IdClass} of some associated entity. + */ private boolean isIdClassPrimaryKeyOfAssociatedEntity( ElementsToProcess elementsToProcess, ClassDetails compositeClass, @@ -639,26 +644,58 @@ private boolean isIdClassPrimaryKeyOfAssociatedEntity( Map inheritanceStates, MetadataBuildingContext context) { if ( elementsToProcess.getIdPropertyCount() == 1 ) { + // There's only one @Id field, so it might be the @EmbeddedId of an associated + // entity referenced via a @ManyToOne or @OneToOne association final PropertyData idPropertyOnBaseClass = getUniqueIdPropertyFromBaseClass( inferredData, baseInferredData, propertyAccessor, context ); - final InheritanceState state = - inheritanceStates.get( idPropertyOnBaseClass.getClassOrElementType().determineRawClass() ); + final TypeDetails idPropertyType = idPropertyOnBaseClass.getClassOrElementType(); + final InheritanceState state = inheritanceStates.get( idPropertyType.determineRawClass() ); if ( state == null ) { - return false; //while it is likely a user error, let's consider it is something that might happen - } - final ClassDetails associatedClassWithIdClass = state.getClassWithIdClass( true ); - if ( associatedClassWithIdClass == null ) { - //we cannot know for sure here unless we try and find the @EmbeddedId - //Let's not do this thorough checking but do some extra validation - return hasToOneAnnotation( idPropertyOnBaseClass.getAttributeMember() ); - + // Likely a user error, but treat it as something that might happen + return false; } else { - final IdClass idClass = associatedClassWithIdClass.getAnnotationUsage( IdClass.class, modelsContext() ); - return compositeClass.getName().equals( idClass.value().getName() ); + final ClassDetails associatedClassWithIdClass = state.getClassWithIdClass( true ); + if ( associatedClassWithIdClass == null ) { + // If annotated @OneToOne or @ManyToOne, it's an association to another entity + return hasToOneAnnotation( idPropertyOnBaseClass.getAttributeMember() ) + // determine if the @Id or @EmbeddedId tpe is the same + && isIdClassOfAssociatedEntity( compositeClass, propertyAccessor, context, idPropertyType ); + } + else { + // The associated entity has an @IdClass, so check if it's the same + final IdClass idClass = + associatedClassWithIdClass.getAnnotationUsage( IdClass.class, modelsContext() ); + return compositeClass.getName().equals( idClass.value().getName() ); + } } } else { + // There are multiple @Id fields, so we know for sure that the id class of + // this entity can't be the identifier type of the associated entity + return false; + } + } + + private static boolean isIdClassOfAssociatedEntity( + ClassDetails compositeClass, + AccessType propertyAccessor, + MetadataBuildingContext context, + TypeDetails idPropertyType) { + // Determine the @Id type or @EmbeddedId class of the associated entity + final var propertyContainer = + new PropertyContainer( idPropertyType.determineRawClass(), idPropertyType, propertyAccessor ); + final List idProperties = new ArrayList<>(); + final int idPropertyCount = addElementsOfClass( idProperties, propertyContainer, context, 0 ); + if ( idPropertyCount == 1 ) { + // Exactly one @Id or @EmbeddedId attribute + final PropertyData idPropertyOfAssociatedEntity = idProperties.get( 0 ); + return compositeClass.getName() + .equals( idPropertyOfAssociatedEntity.getPropertyType().getName() ); + } + else { + // No id property found in the associated class, + // or multiple id properties but no @IdClass return false; } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/InheritanceState.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/InheritanceState.java index 862d60b3ec65..483dd8a7899d 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/InheritanceState.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/InheritanceState.java @@ -11,7 +11,6 @@ import org.hibernate.AnnotationException; import org.hibernate.boot.spi.AccessType; -import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.PropertyData; import org.hibernate.mapping.Component; @@ -112,7 +111,7 @@ public static InheritanceState getSuperclassInheritanceState( ClassDetails superclass = classDetails; do { superclass = superclass.getSuperClass(); - InheritanceState currentState = states.get( superclass ); + final var currentState = states.get( superclass ); if ( currentState != null ) { return currentState; } @@ -225,7 +224,7 @@ public Boolean hasIdClassOrEmbeddedId() { */ private ElementsToProcess getElementsToProcess() { if ( elementsToProcess == null ) { - final InheritanceState inheritanceState = inheritanceStatePerClass.get( classDetails ); + final var inheritanceState = inheritanceStatePerClass.get( classDetails ); assert !inheritanceState.isEmbeddableSuperclass(); getMappedSuperclassesTillNextEntityOrdered(); @@ -304,14 +303,14 @@ private void getMappedSuperclassesTillNextEntityOrdered() { } private void addMappedSuperClassInMetadata(Component component) { - org.hibernate.mapping.MappedSuperclass mappedSuperclass = processMappedSuperclass( component.getTable() ); + final var mappedSuperclass = processMappedSuperclass( component.getTable() ); if ( mappedSuperclass != null ) { component.setMappedSuperclass( mappedSuperclass ); } } private void addMappedSuperClassInMetadata(PersistentClass persistentClass) { - org.hibernate.mapping.MappedSuperclass mappedSuperclass = processMappedSuperclass( persistentClass.getImplicitTable() ); + final var mappedSuperclass = processMappedSuperclass( persistentClass.getImplicitTable() ); if ( mappedSuperclass != null ) { persistentClass.setSuperMappedSuperclass( mappedSuperclass ); } @@ -320,8 +319,8 @@ private void addMappedSuperClassInMetadata(PersistentClass persistentClass) { private org.hibernate.mapping.MappedSuperclass processMappedSuperclass(Table implicitTable) { //add @MappedSuperclass in the metadata // classes from 0 to n-1 are @MappedSuperclass and should be linked - final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector(); - final InheritanceState superEntityState = getInheritanceStateOfSuperEntity( classDetails, inheritanceStatePerClass ); + final var metadataCollector = buildingContext.getMetadataCollector(); + final var superEntityState = getInheritanceStateOfSuperEntity( classDetails, inheritanceStatePerClass ); final PersistentClass superEntity = superEntityState != null ? metadataCollector.getEntityBinding( superEntityState.getClassDetails().getName() ) @@ -329,10 +328,10 @@ private org.hibernate.mapping.MappedSuperclass processMappedSuperclass(Table imp final int lastMappedSuperclass = classesToProcessForMappedSuperclass.size() - 1; org.hibernate.mapping.MappedSuperclass mappedSuperclass = null; for ( int index = 0; index < lastMappedSuperclass; index++ ) { - org.hibernate.mapping.MappedSuperclass parentSuperclass = mappedSuperclass; + final var parentSuperclass = mappedSuperclass; // todo (jpa32) : causes the mapped-superclass Class reference to be loaded... // - but this is how it's always worked, so... - final ClassDetails mappedSuperclassDetails = classesToProcessForMappedSuperclass.get( index ); + final var mappedSuperclassDetails = classesToProcessForMappedSuperclass.get( index ); final Class mappedSuperclassJavaType = mappedSuperclassDetails.toJavaClass(); //add MappedSuperclass if not already there mappedSuperclass = metadataCollector.getMappedSuperclass( mappedSuperclassJavaType ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/ScanningCoordinator.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/ScanningCoordinator.java index 76651ce90cfc..a6bc6eae4043 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/ScanningCoordinator.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/ScanningCoordinator.java @@ -4,12 +4,6 @@ */ package org.hibernate.boot.model.process.internal; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - import org.hibernate.boot.MappingException; import org.hibernate.boot.archive.internal.StandardArchiveDescriptorFactory; import org.hibernate.boot.archive.internal.UrlInputStreamAccess; @@ -26,6 +20,8 @@ import org.hibernate.boot.internal.ClassLoaderAccessImpl; import org.hibernate.boot.jaxb.Origin; import org.hibernate.boot.jaxb.SourceType; +import org.hibernate.boot.jaxb.internal.InputStreamAccessXmlSource; +import org.hibernate.boot.jaxb.spi.Binding; import org.hibernate.boot.model.convert.internal.ConverterDescriptors; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; @@ -33,6 +29,12 @@ import org.hibernate.boot.spi.ClassLoaderAccess; import org.hibernate.boot.spi.XmlMappingBinderAccess; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import static org.hibernate.boot.archive.scan.internal.ScannerLogger.SCANNER_LOGGER; /** @@ -222,17 +224,27 @@ public void applyScanResultsToManagedResources( if ( xmlMappingBinderAccess != null ) { // xml mapping is not disabled for ( MappingFileDescriptor mappingFileDescriptor : scanResult.getLocatedMappingFiles() ) { - managedResources.addXmlBinding( xmlMappingBinderAccess.bind( mappingFileDescriptor.getStreamAccess() ) ); + //noinspection unchecked,rawtypes + managedResources.addXmlBinding( (Binding) InputStreamAccessXmlSource.fromStreamAccess( + mappingFileDescriptor.getStreamAccess(), + xmlMappingBinderAccess.getMappingBinder() + ) ); nonLocatedMappingFileNames.remove( mappingFileDescriptor.getName() ); } for ( String name : nonLocatedMappingFileNames ) { + final Origin origin = new Origin( SourceType.RESOURCE, name ); final URL url = classLoaderService.locateResource( name ); if ( url == null ) { - throw new MappingException( "Unable to resolve explicitly named mapping file: " + name, - new Origin( SourceType.RESOURCE, name ) ); + throw new MappingException( "Unable to resolve explicitly named mapping file: " + name, origin ); } - managedResources.addXmlBinding( xmlMappingBinderAccess.bind( new UrlInputStreamAccess( url ) ) ); + final UrlInputStreamAccess urlInputStreamAccess = new UrlInputStreamAccess( url ); + //noinspection unchecked,rawtypes + managedResources.addXmlBinding( (Binding) InputStreamAccessXmlSource.fromStreamAccess( + urlInputStreamAccess, + origin, + xmlMappingBinderAccess.getMappingBinder() + ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/XmlMappingBinderAccess.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/XmlMappingBinderAccess.java index 896f6d1ee9c6..41e2aa140bc8 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/XmlMappingBinderAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/XmlMappingBinderAccess.java @@ -4,37 +4,31 @@ */ package org.hibernate.boot.spi; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.function.Function; - -import org.hibernate.boot.MappingNotFoundException; import org.hibernate.boot.archive.spi.InputStreamAccess; -import org.hibernate.boot.jaxb.Origin; -import org.hibernate.boot.jaxb.SourceType; import org.hibernate.boot.jaxb.internal.FileXmlSource; +import org.hibernate.boot.jaxb.internal.InputStreamAccessXmlSource; import org.hibernate.boot.jaxb.internal.InputStreamXmlSource; import org.hibernate.boot.jaxb.internal.MappingBinder; import org.hibernate.boot.jaxb.internal.UrlXmlSource; -import org.hibernate.boot.jaxb.spi.JaxbBindableMappingDescriptor; import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.boot.jaxb.spi.JaxbBindableMappingDescriptor; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.service.ServiceRegistry; -import org.jboss.logging.Logger; +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.function.Function; /** - * Holds the XML binder and a classloader used for binding mappings. + * Holds the XML binder and a classloader used for binding mappings, as well + * as access to methods to perform binding of sources of mapping XML. * * @apiNote This class is very poorly named. * * @author Steve Ebersole */ public class XmlMappingBinderAccess { - private static final Logger LOG = Logger.getLogger( XmlMappingBinderAccess.class ); - private final ClassLoaderService classLoaderService; private final MappingBinder mappingBinder; @@ -54,65 +48,51 @@ public MappingBinder getMappingBinder() { /** * Create a {@linkplain Binding binding} from a named URL resource + * + * @see UrlXmlSource#fromUrl */ public Binding bind(String resource) { - LOG.tracef( "Reading mappings from resource: %s", resource ); - final Origin origin = new Origin( SourceType.RESOURCE, resource ); - final URL url = classLoaderService.locateResource( resource ); - if ( url == null ) { - throw new MappingNotFoundException( origin ); - } - return new UrlXmlSource( origin, url ).doBind( getMappingBinder() ); + //noinspection unchecked,rawtypes + return (Binding) UrlXmlSource.fromResource( resource, classLoaderService, getMappingBinder() ); } /** * Create a {@linkplain Binding binding} from a File reference + * + * @see FileXmlSource#fromFile */ public Binding bind(File file) { - final Origin origin = new Origin( SourceType.FILE, file.getPath() ); - LOG.tracef( "Reading mappings from file: %s", origin.getName() ); - if ( !file.exists() ) { - throw new MappingNotFoundException( origin ); - } - return new FileXmlSource( origin, file ).doBind( getMappingBinder() ); + //noinspection unchecked,rawtypes + return (Binding) FileXmlSource.fromFile( file, getMappingBinder() ); } /** * Create a {@linkplain Binding binding} from an input stream + * + * @see InputStreamAccessXmlSource#fromStreamAccess */ public Binding bind(InputStreamAccess xmlInputStreamAccess) { - LOG.tracef( "Reading mappings from InputStreamAccess: %s", xmlInputStreamAccess.getStreamName() ); - final Origin origin = new Origin( SourceType.INPUT_STREAM, xmlInputStreamAccess.getStreamName() ); - final InputStream xmlInputStream = xmlInputStreamAccess.accessInputStream(); - try { - return new InputStreamXmlSource( origin, xmlInputStream, false ).doBind( mappingBinder ); - } - finally { - try { - xmlInputStream.close(); - } - catch (IOException e) { - LOG.debugf( "Unable to close InputStream obtained from InputStreamAccess : %s", xmlInputStreamAccess.getStreamName() ); - } - } + //noinspection unchecked,rawtypes + return (Binding) InputStreamAccessXmlSource.fromStreamAccess( xmlInputStreamAccess, getMappingBinder() ); } /** * Create a {@linkplain Binding binding} from an input stream + * + * @see InputStreamXmlSource#fromStream */ public Binding bind(InputStream xmlInputStream) { - LOG.trace( "Reading mappings from InputStream" ); - final Origin origin = new Origin( SourceType.INPUT_STREAM, null ); - return new InputStreamXmlSource( origin, xmlInputStream, false ).doBind( getMappingBinder() ); + //noinspection unchecked,rawtypes + return (Binding) InputStreamXmlSource.fromStream( xmlInputStream, getMappingBinder() ); } /** * Create a {@linkplain Binding binding} from a URL + * + * @see UrlXmlSource#fromUrl */ public Binding bind(URL url) { - final String urlExternalForm = url.toExternalForm(); - LOG.tracef( "Reading mapping document from URL: %s", urlExternalForm ); - final Origin origin = new Origin( SourceType.URL, urlExternalForm ); - return new UrlXmlSource( origin, url ).doBind( getMappingBinder() ); + //noinspection unchecked,rawtypes + return (Binding) UrlXmlSource.fromUrl( url, getMappingBinder() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/JdbcSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/JdbcSettings.java index 12b686ccf47d..4c59d442ecd6 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/JdbcSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/JdbcSettings.java @@ -246,12 +246,12 @@ public interface JdbcSettings extends C3p0Settings, AgroalSettings, HikariCPSett * automatically: *
    *
  • if {@link #JAKARTA_JTA_DATASOURCE} or {@link #JAKARTA_NON_JTA_DATASOURCE} - * is set, {@linkplain org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl + * is set, {@linkplain org.hibernate.engine.jdbc.connections.internal.DataSourceConnectionProvider * a datasource-based implementation} is used; *
  • otherwise, a {@code ConnectionProvider} is loaded automatically as a * {@linkplain java.util.ServiceLoader Java service}; *
  • but if no service is found, or if more than one service is available, - * {@linkplain org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl + * {@linkplain org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider * a default implementation} is used as a fallback. *
*

@@ -266,7 +266,7 @@ public interface JdbcSettings extends C3p0Settings, AgroalSettings, HikariCPSett * Specifies the maximum number of inactive connections for any * {@linkplain ConnectionProvider connection pool} which respects this * setting, including every built-in implementation except for - * {@link org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl}. + * {@link org.hibernate.engine.jdbc.connections.internal.DataSourceConnectionProvider}. *

* The default pool size depends on the connection provider. */ @@ -276,7 +276,7 @@ public interface JdbcSettings extends C3p0Settings, AgroalSettings, HikariCPSett * Specifies the JDBC transaction isolation level for connections obtained * from any {@link ConnectionProvider} implementation which respects this * setting, including every built-in implementation except for - * {@link org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl}. + * {@link org.hibernate.engine.jdbc.connections.internal.DataSourceConnectionProvider}. *

* Possible values are enumerated by {@link java.sql.Connection}: * {@code READ_UNCOMMITTED}, {@code READ_COMMITTED}, @@ -293,7 +293,7 @@ public interface JdbcSettings extends C3p0Settings, AgroalSettings, HikariCPSett * Controls the autocommit mode of JDBC connections obtained from any * {@link ConnectionProvider} implementation which respects this setting, * including every built-in implementation except for - * {@link org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl}. + * {@link org.hibernate.engine.jdbc.connections.internal.DataSourceConnectionProvider}. * * @see java.sql.Connection#setAutoCommit(boolean) * @@ -329,7 +329,7 @@ public interface JdbcSettings extends C3p0Settings, AgroalSettings, HikariCPSett * append {@code foo=bar} to the JDBC connection URL. * * @deprecated This setting is only supported by {@code C3P0ConnectionProvider} - * and {@link org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl}. + * and {@link org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider}. */ @Deprecated(since="7") String CONNECTION_PREFIX = "hibernate.connection"; 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 440cc014f3c6..5c11e834894d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -67,7 +67,6 @@ import org.hibernate.type.descriptor.jdbc.BlobJdbcType; import org.hibernate.type.descriptor.jdbc.ClobJdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType; -import org.hibernate.type.descriptor.jdbc.NClobJdbcType; import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType; import org.hibernate.type.descriptor.jdbc.UUIDJdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; @@ -384,7 +383,7 @@ protected void contributeCockroachTypes(TypeContributions typeContributions, Ser // Force Blob binding to byte[] for CockroachDB jdbcTypeRegistry.addDescriptor( Types.BLOB, BlobJdbcType.MATERIALIZED ); jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.MATERIALIZED ); - jdbcTypeRegistry.addDescriptor( Types.NCLOB, NClobJdbcType.MATERIALIZED ); + jdbcTypeRegistry.addDescriptor( Types.NCLOB, ClobJdbcType.MATERIALIZED ); // The next two contributions are the same as for Postgresql typeContributions.contributeJdbcType( ObjectNullAsBinaryTypeJdbcType.INSTANCE ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/sequence/MariaDBSequenceSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/sequence/MariaDBSequenceSupport.java index 2d80f13fd7ca..6ef57d4b5e34 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/sequence/MariaDBSequenceSupport.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/sequence/MariaDBSequenceSupport.java @@ -35,6 +35,11 @@ public String getSelectSequencePreviousValString(String sequenceName) throws Map return "previous value for " + sequenceName; } + @Override + public String getDropSequenceString(String sequenceName) throws MappingException { + return "drop sequence if exists " + sequenceName; + } + @Override public boolean sometimesNeedsStartingValue() { return true; 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 aa7b879477ea..4d9fb06c5fbd 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 @@ -140,7 +140,7 @@ private ConnectionProvider instantiateNamedConnectionProvider( private ConnectionProvider instantiateConnectionProvider( Map configurationValues, StrategySelector strategySelector, BeanContainer beanContainer) { if ( configurationValues.containsKey( DATASOURCE ) ) { - return new DatasourceConnectionProviderImpl(); + return new DataSourceConnectionProvider(); } final Class singleRegisteredProvider = @@ -163,7 +163,7 @@ else if ( hasConfiguration( configurationValues, AGROAL_CONFIG_PREFIX ) ) { return instantiateProvider( strategySelector, AGROAL_STRATEGY ); } else if ( configurationValues.containsKey( URL ) ) { - return new DriverManagerConnectionProviderImpl(); + return new DriverManagerConnectionProvider(); } else { if ( beanContainer != null ) { 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 new file mode 100644 index 000000000000..f9f27ad5e9ef --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DataSourceConnectionProvider.java @@ -0,0 +1,192 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.jdbc.connections.internal; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; +import javax.sql.DataSource; + +import org.hibernate.HibernateException; +import org.hibernate.cfg.JdbcSettings; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProviderConfigurationException; +import org.hibernate.engine.jdbc.connections.spi.DatabaseConnectionInfo; +import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData; +import org.hibernate.engine.jndi.spi.JndiService; +import org.hibernate.internal.log.ConnectionInfoLogger; +import org.hibernate.service.UnknownUnwrapTypeException; +import org.hibernate.service.spi.Configurable; +import org.hibernate.service.spi.InjectService; +import org.hibernate.service.spi.Stoppable; + +import static org.hibernate.cfg.JdbcSettings.DATASOURCE; +import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.toIsolationNiceName; + +/** + * A {@link ConnectionProvider} that manages connections from an underlying {@link DataSource}. + *

+ * The {@link DataSource} to use may be specified by either:

    + *
  • injection using {@link #setDataSource}, + *
  • passing the {@link DataSource} instance using {@value JdbcSettings#DATASOURCE}, + * {@value JdbcSettings#JAKARTA_JTA_DATASOURCE}, or {@value JdbcSettings#JAKARTA_NON_JTA_DATASOURCE}, or + *
  • declaring the JNDI name under which the {@link DataSource} is found via {@value JdbcSettings#DATASOURCE}, + * {@value JdbcSettings#JAKARTA_JTA_DATASOURCE}, or {@value JdbcSettings#JAKARTA_NON_JTA_DATASOURCE}. + *
+ * + * @author Gavin King + * @author Steve Ebersole + */ +public class DataSourceConnectionProvider + implements ConnectionProvider, Configurable, Stoppable { + + private DataSource dataSource; + private String user; + private String pass; + private boolean useCredentials; + private JndiService jndiService; + private String dataSourceJndiName; + + private boolean available; + + public DataSource getDataSource() { + return dataSource; + } + + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } + + @InjectService( required = false ) + @SuppressWarnings("unused") + public void setJndiService(JndiService jndiService) { + this.jndiService = jndiService; + } + + @Override + public boolean isUnwrappableAs(Class unwrapType) { + return unwrapType.isAssignableFrom( DataSourceConnectionProvider.class ) + || unwrapType.isAssignableFrom( DataSource.class); + } + + @Override + @SuppressWarnings("unchecked") + public T unwrap(Class unwrapType) { + if ( unwrapType.isAssignableFrom( DataSourceConnectionProvider.class ) ) { + return (T) this; + } + else if ( unwrapType.isAssignableFrom( DataSource.class) ) { + return (T) getDataSource(); + } + else { + throw new UnknownUnwrapTypeException( unwrapType ); + } + } + + @Override + public void configure(Map configuration) { + if ( dataSource == null ) { + final Object dataSourceSetting = configuration.get( DATASOURCE ); + if ( dataSourceSetting instanceof DataSource instance ) { + dataSource = instance; + } + else if ( dataSourceSetting instanceof String jndiName ) { + dataSourceJndiName = jndiName; + if ( jndiService == null ) { + throw new ConnectionProviderConfigurationException( "Unable to locate JndiService to lookup Datasource" ); + } + dataSource = (DataSource) jndiService.locate( jndiName ); + } + else { + throw new ConnectionProviderConfigurationException( + "DataSource to use was not injected nor specified by '" + DATASOURCE + "'" ); + } + } + if ( dataSource == null ) { + throw new ConnectionProviderConfigurationException( "Unable to determine appropriate DataSource to use" ); + } + + if ( configuration.containsKey( JdbcSettings.AUTOCOMMIT ) ) { + ConnectionInfoLogger.INSTANCE.ignoredSetting( JdbcSettings.AUTOCOMMIT, + DataSourceConnectionProvider.class ); + } + if ( configuration.containsKey( JdbcSettings.ISOLATION ) ) { + ConnectionInfoLogger.INSTANCE.ignoredSetting( JdbcSettings.ISOLATION, + DataSourceConnectionProvider.class ); + } + + user = (String) configuration.get( JdbcSettings.USER ); + pass = (String) configuration.get( JdbcSettings.PASS ); + useCredentials = user != null || pass != null; + available = true; + } + + @Override + public void stop() { + available = false; + dataSource = null; + } + + @Override + public Connection getConnection() throws SQLException { + if ( !available ) { + throw new HibernateException( "Provider is closed" ); + } + return useCredentials ? dataSource.getConnection( user, pass ) : dataSource.getConnection(); + } + + @Override + public void closeConnection(Connection connection) throws SQLException { + connection.close(); + } + + @Override + public boolean supportsAggressiveRelease() { + return true; + } + + @Override + public DatabaseConnectionInfo getDatabaseConnectionInfo(Dialect dialect) { + return getDatabaseConnectionInfo( dialect, null ); + } + + @Override + public DatabaseConnectionInfo getDatabaseConnectionInfo(Dialect dialect, ExtractedDatabaseMetaData metaData) { + return new DatabaseConnectionInfoImpl( + DataSourceConnectionProvider.class, + metaData == null ? null : metaData.getUrl(), + metaData == null ? null : metaData.getDriver(), + dialect.getClass(), + dialect.getVersion(), + metaData == null || metaData.supportsSchemas(), + metaData == null || metaData.supportsCatalogs(), + metaData == null ? null : metaData.getConnectionSchemaName(), + metaData == null ? null : metaData.getConnectionCatalogName(), + null, + metaData == null ? null : isolationString( metaData ), + null, + null, + metaData != null ? fetchSize( metaData ) : null + ) { + @Override + public String toInfoString() { + return dataSourceJndiName != null + ? "\tDataSource JNDI name [" + dataSourceJndiName + "]\n" + super.toInfoString() + : super.toInfoString(); + } + }; + } + + private static Integer fetchSize(ExtractedDatabaseMetaData metaData) { + final int defaultFetchSize = metaData.getDefaultFetchSize(); + return defaultFetchSize == -1 ? null : defaultFetchSize; + } + + private String isolationString(ExtractedDatabaseMetaData metaData) { + return toIsolationNiceName( metaData.getTransactionIsolation() ) + + " [default " + toIsolationNiceName( metaData.getDefaultTransactionIsolation() ) + "]"; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DatasourceConnectionProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DatasourceConnectionProviderImpl.java index 6877fa533e94..5595eb0b0ebf 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DatasourceConnectionProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DatasourceConnectionProviderImpl.java @@ -4,189 +4,9 @@ */ package org.hibernate.engine.jdbc.connections.internal; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.Map; -import javax.sql.DataSource; - -import org.hibernate.HibernateException; -import org.hibernate.cfg.JdbcSettings; -import org.hibernate.dialect.Dialect; -import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; -import org.hibernate.engine.jdbc.connections.spi.ConnectionProviderConfigurationException; -import org.hibernate.engine.jdbc.connections.spi.DatabaseConnectionInfo; -import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData; -import org.hibernate.engine.jndi.spi.JndiService; -import org.hibernate.internal.log.ConnectionInfoLogger; -import org.hibernate.service.UnknownUnwrapTypeException; -import org.hibernate.service.spi.Configurable; -import org.hibernate.service.spi.InjectService; -import org.hibernate.service.spi.Stoppable; - -import static org.hibernate.cfg.JdbcSettings.DATASOURCE; -import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.toIsolationNiceName; - /** - * A {@link ConnectionProvider} that manages connections from an underlying {@link DataSource}. - *

- * The {@link DataSource} to use may be specified by either:

    - *
  • injection using {@link #setDataSource}, - *
  • passing the {@link DataSource} instance using {@value JdbcSettings#DATASOURCE}, - * {@value JdbcSettings#JAKARTA_JTA_DATASOURCE}, or {@value JdbcSettings#JAKARTA_NON_JTA_DATASOURCE}, or - *
  • declaring the JNDI name under which the {@link DataSource} is found via {@value JdbcSettings#DATASOURCE}, - * {@value JdbcSettings#JAKARTA_JTA_DATASOURCE}, or {@value JdbcSettings#JAKARTA_NON_JTA_DATASOURCE}. - *
- * - * @author Gavin King - * @author Steve Ebersole + * @deprecated Use {@link DataSourceConnectionProvider} */ -public class DatasourceConnectionProviderImpl - implements ConnectionProvider, Configurable, Stoppable { - - private DataSource dataSource; - private String user; - private String pass; - private boolean useCredentials; - private JndiService jndiService; - private String dataSourceJndiName; - - private boolean available; - - public DataSource getDataSource() { - return dataSource; - } - - public void setDataSource(DataSource dataSource) { - this.dataSource = dataSource; - } - - @InjectService( required = false ) - @SuppressWarnings("unused") - public void setJndiService(JndiService jndiService) { - this.jndiService = jndiService; - } - - @Override - public boolean isUnwrappableAs(Class unwrapType) { - return unwrapType.isAssignableFrom( DatasourceConnectionProviderImpl.class ) - || unwrapType.isAssignableFrom( DataSource.class); - } - - @Override - @SuppressWarnings("unchecked") - public T unwrap(Class unwrapType) { - if ( unwrapType.isAssignableFrom( DatasourceConnectionProviderImpl.class ) ) { - return (T) this; - } - else if ( unwrapType.isAssignableFrom( DataSource.class) ) { - return (T) getDataSource(); - } - else { - throw new UnknownUnwrapTypeException( unwrapType ); - } - } - - @Override - public void configure(Map configuration) { - if ( dataSource == null ) { - final Object dataSourceSetting = configuration.get( DATASOURCE ); - if ( dataSourceSetting instanceof DataSource instance ) { - dataSource = instance; - } - else if ( dataSourceSetting instanceof String jndiName ) { - dataSourceJndiName = jndiName; - if ( jndiService == null ) { - throw new ConnectionProviderConfigurationException( "Unable to locate JndiService to lookup Datasource" ); - } - dataSource = (DataSource) jndiService.locate( jndiName ); - } - else { - throw new ConnectionProviderConfigurationException( - "DataSource to use was not injected nor specified by '" + DATASOURCE + "'" ); - } - } - if ( dataSource == null ) { - throw new ConnectionProviderConfigurationException( "Unable to determine appropriate DataSource to use" ); - } - - if ( configuration.containsKey( JdbcSettings.AUTOCOMMIT ) ) { - ConnectionInfoLogger.INSTANCE.ignoredSetting( JdbcSettings.AUTOCOMMIT, - DatasourceConnectionProviderImpl.class ); - } - if ( configuration.containsKey( JdbcSettings.ISOLATION ) ) { - ConnectionInfoLogger.INSTANCE.ignoredSetting( JdbcSettings.ISOLATION, - DatasourceConnectionProviderImpl.class ); - } - - user = (String) configuration.get( JdbcSettings.USER ); - pass = (String) configuration.get( JdbcSettings.PASS ); - useCredentials = user != null || pass != null; - available = true; - } - - @Override - public void stop() { - available = false; - dataSource = null; - } - - @Override - public Connection getConnection() throws SQLException { - if ( !available ) { - throw new HibernateException( "Provider is closed" ); - } - return useCredentials ? dataSource.getConnection( user, pass ) : dataSource.getConnection(); - } - - @Override - public void closeConnection(Connection connection) throws SQLException { - connection.close(); - } - - @Override - public boolean supportsAggressiveRelease() { - return true; - } - - @Override - public DatabaseConnectionInfo getDatabaseConnectionInfo(Dialect dialect) { - return getDatabaseConnectionInfo( dialect, null ); - } - - @Override - public DatabaseConnectionInfo getDatabaseConnectionInfo(Dialect dialect, ExtractedDatabaseMetaData metaData) { - return new DatabaseConnectionInfoImpl( - DatasourceConnectionProviderImpl.class, - metaData == null ? null : metaData.getUrl(), - metaData == null ? null : metaData.getDriver(), - dialect.getClass(), - dialect.getVersion(), - metaData == null || metaData.supportsSchemas(), - metaData == null || metaData.supportsCatalogs(), - metaData == null ? null : metaData.getConnectionSchemaName(), - metaData == null ? null : metaData.getConnectionCatalogName(), - null, - metaData == null ? null : isolationString( metaData ), - null, - null, - metaData != null ? fetchSize( metaData ) : null - ) { - @Override - public String toInfoString() { - return dataSourceJndiName != null - ? "\tDataSource JNDI name [" + dataSourceJndiName + "]\n" + super.toInfoString() - : super.toInfoString(); - } - }; - } - - private static Integer fetchSize(ExtractedDatabaseMetaData metaData) { - final int defaultFetchSize = metaData.getDefaultFetchSize(); - return defaultFetchSize == -1 ? null : defaultFetchSize; - } - - private String isolationString(ExtractedDatabaseMetaData metaData) { - return toIsolationNiceName( metaData.getTransactionIsolation() ) - + " [default " + toIsolationNiceName( metaData.getDefaultTransactionIsolation() ) + "]"; - } +@Deprecated(since = "7.1", forRemoval = true) +public class DatasourceConnectionProviderImpl extends DriverManagerConnectionProvider { } 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 new file mode 100644 index 000000000000..168e78865dae --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProvider.java @@ -0,0 +1,372 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.jdbc.connections.internal; + +import java.sql.Connection; +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Map; +import java.util.Properties; + +import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; +import org.hibernate.dialect.Database; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.SimpleDatabaseVersion; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProviderConfigurationException; +import org.hibernate.engine.jdbc.connections.spi.DatabaseConnectionInfo; +import org.hibernate.exception.JDBCConnectionException; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.service.UnknownUnwrapTypeException; +import org.hibernate.service.spi.Configurable; +import org.hibernate.service.spi.ServiceException; +import org.hibernate.service.spi.ServiceRegistryAwareService; +import org.hibernate.service.spi.ServiceRegistryImplementor; +import org.hibernate.service.spi.Stoppable; +import org.hibernate.internal.log.ConnectionInfoLogger; + +import static org.hibernate.cfg.JdbcSettings.AUTOCOMMIT; +import static org.hibernate.cfg.JdbcSettings.DRIVER; +import static org.hibernate.cfg.JdbcSettings.JAKARTA_JDBC_URL; +import static org.hibernate.cfg.JdbcSettings.POOL_SIZE; +import static org.hibernate.cfg.JdbcSettings.URL; +import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.extractIsolation; +import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.getConnectionProperties; +import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.toIsolationNiceName; +import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getCatalog; +import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getDriverName; +import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getFetchSize; +import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getIsolation; +import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getSchema; +import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.hasCatalog; +import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.hasSchema; +import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean; +import static org.hibernate.internal.util.config.ConfigurationHelper.getInt; +import static org.hibernate.internal.util.config.ConfigurationHelper.getLong; +import static org.hibernate.internal.util.config.ConfigurationHelper.getString; + +/** + * A connection provider that uses the {@link DriverManager} directly to open connections and provides + * a very rudimentary connection pool. + * + * @implNote Not intended for use in production systems! + * + * @author Gavin King + * @author Steve Ebersole + */ +public class DriverManagerConnectionProvider + implements ConnectionProvider, Configurable, Stoppable, ServiceRegistryAwareService, ConnectionValidator { + + public static final String MIN_SIZE = "hibernate.connection.min_pool_size"; + public static final String INITIAL_SIZE = "hibernate.connection.initial_pool_size"; + // in TimeUnit.SECONDS + public static final String VALIDATION_INTERVAL = "hibernate.connection.pool_validation_interval"; + public static final String INIT_SQL ="hibernate.connection.init_sql"; + public static final String CONNECTION_CREATOR_FACTORY ="hibernate.connection.creator_factory_class"; + + private volatile PoolState state; + + private static DatabaseConnectionInfo dbInfo; + + // create the pool ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + private volatile ServiceRegistry serviceRegistry; + + @Override + public void injectServices(ServiceRegistryImplementor serviceRegistry) { + this.serviceRegistry = serviceRegistry; + } + + @Override + public void configure(Map configurationValues) { + ConnectionInfoLogger.INSTANCE.usingHibernateBuiltInConnectionPool(); + final PooledConnections pool = buildPool( configurationValues, serviceRegistry ); + final long validationInterval = getLong( VALIDATION_INTERVAL, configurationValues, 30 ); + state = new PoolState( pool, validationInterval ); + } + + private PooledConnections buildPool(Map configuration, ServiceRegistry serviceRegistry) { + // connection settings + final String url = jdbcUrl( configuration ); + final String driverClassName = getString( DRIVER, configuration ); + final Properties connectionProps = getConnectionProperties( configuration ); + final boolean autoCommit = getBoolean( AUTOCOMMIT, configuration ); // default autocommit to false + final Integer isolation = extractIsolation( configuration ); + final String initSql = getString( INIT_SQL, configuration ); + + // pool settings + final int minSize = getInt( MIN_SIZE, configuration, 1 ); + final int maxSize = getInt( POOL_SIZE, configuration, 20 ); + final int initialSize = getInt( INITIAL_SIZE, configuration, minSize ); + + final Driver driver = loadDriver( driverClassName, serviceRegistry, url ); + if ( driver == null ) { + //we're hoping that the driver is already loaded + logAvailableDrivers(); + } + + final var connectionCreator = + getConnectionCreatorFactory( configuration, serviceRegistry ) + .create( + driver, + serviceRegistry, + url, + connectionProps, + autoCommit, + isolation, + initSql, + configuration + ); + + try ( var connection = connectionCreator.createConnection() ) { + dbInfo = new DatabaseConnectionInfoImpl( + DriverManagerConnectionProvider.class, + url, + getDriverName( connection ), + null, + SimpleDatabaseVersion.ZERO_VERSION, + hasSchema( connection ), + hasCatalog( connection ), + getSchema( connection ), + getCatalog( connection ), + Boolean.toString( autoCommit ), + isolation != null + ? toIsolationNiceName( isolation ) + : toIsolationNiceName( getIsolation( connection ) ), + minSize, + maxSize, + getFetchSize( connection ) + ); + if ( !connection.getAutoCommit() ) { + connection.rollback(); + } + } + catch (SQLException e) { + throw new JDBCConnectionException( "Could not create connection", e ); + } + + return new PooledConnections.Builder( connectionCreator ) + .autoCommit( autoCommit ) + .initialSize( initialSize ) + .minSize( minSize ) + .maxSize( maxSize ) + .validator( this ) + .build(); + } + + private static Driver loadDriver(String driverClassName, ServiceRegistry serviceRegistry, String url) { + if ( driverClassName != null ) { + return loadDriverIfPossible( driverClassName, serviceRegistry ); + } + else { + // try to guess the driver class from the JDBC URL + for ( var database: Database.values() ) { + if ( database.matchesUrl( url ) ) { + final String databaseDriverClassName = database.getDriverClassName( url ); + if ( databaseDriverClassName != null ) { + try { + return loadDriverIfPossible( databaseDriverClassName, serviceRegistry ); + } + catch (Exception e) { + // swallow it, since this was not an explicit setting by the user + } + } + } + } + return null; + } + } + + private static void logAvailableDrivers() { + ConnectionInfoLogger.INSTANCE.jdbcDriverNotSpecified(); + final var list = new StringBuilder(); + DriverManager.drivers() + .forEach( driver -> { + if ( !list.isEmpty() ) { + list.append( ", " ); + } + list.append( driver.getClass().getName() ); + } ); + ConnectionInfoLogger.INSTANCE.availableJdbcDrivers( list.toString() ); + } + + private static String jdbcUrl(Map configuration) { + final String url = (String) configuration.get( URL ); + if ( url == null ) { + throw new ConnectionProviderConfigurationException( "No JDBC URL specified by property '" + JAKARTA_JDBC_URL + "'" ); + } + return url; + } + + private static ConnectionCreatorFactory getConnectionCreatorFactory( + Map configuration, ServiceRegistry serviceRegistry) { + final Object connectionCreatorFactory = configuration.get( CONNECTION_CREATOR_FACTORY ); + final ConnectionCreatorFactory factory; + if ( connectionCreatorFactory instanceof ConnectionCreatorFactory instance ) { + factory = instance; + } + else if ( connectionCreatorFactory != null ) { + factory = loadConnectionCreatorFactory( connectionCreatorFactory.toString(), serviceRegistry ); + } + else { + factory = null; + } + return factory == null ? ConnectionCreatorFactoryImpl.INSTANCE : factory; + } + + private static Driver loadDriverIfPossible(String driverClassName, ServiceRegistry serviceRegistry) { + if ( driverClassName == null ) { + ConnectionInfoLogger.INSTANCE.debug( "No driver class specified" ); + return null; + } + else if ( serviceRegistry != null ) { + final Class driverClass = + serviceRegistry.requireService( ClassLoaderService.class ) + .classForName( driverClassName ); + try { + return driverClass.newInstance(); + } + catch ( Exception e ) { + throw new ServiceException( "Specified JDBC Driver " + driverClassName + " could not be loaded", e ); + } + } + else { + try { + return (Driver) Class.forName( driverClassName ).newInstance(); + } + catch (Exception e1) { + throw new ServiceException( "Specified JDBC Driver " + driverClassName + " could not be loaded", e1 ); + } + } + } + + private static ConnectionCreatorFactory loadConnectionCreatorFactory( + String connectionCreatorFactoryClassName, ServiceRegistry serviceRegistry) { + if ( connectionCreatorFactoryClassName == null ) { + ConnectionInfoLogger.INSTANCE.debug( "No connection creator factory class specified" ); + return null; + } + else if ( serviceRegistry != null ) { + final Class factoryClass = + serviceRegistry.requireService( ClassLoaderService.class ) + .classForName( connectionCreatorFactoryClassName ); + try { + return factoryClass.newInstance(); + } + catch ( Exception e ) { + throw new ServiceException( "Specified ConnectionCreatorFactory " + connectionCreatorFactoryClassName + + " could not be loaded", e ); + } + } + else { + try { + return (ConnectionCreatorFactory) Class.forName( connectionCreatorFactoryClassName ).newInstance(); + } + catch (Exception e1) { + throw new ServiceException( "Specified ConnectionCreatorFactory " + connectionCreatorFactoryClassName + + " could not be loaded", e1 ); + } + } + } + + + // use the pool ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Override + public Connection getConnection() throws SQLException { + if ( state == null ) { + throw new IllegalStateException( "Cannot get a connection as the driver manager is not properly initialized" ); + } + return state.getConnection(); + } + + @Override + public void closeConnection(Connection connection) throws SQLException { + if ( state == null ) { + throw new IllegalStateException( "Cannot close a connection as the driver manager is not properly initialized" ); + } + state.closeConnection( connection ); + } + + @Override + public boolean supportsAggressiveRelease() { + return false; + } + + @Override + public DatabaseConnectionInfo getDatabaseConnectionInfo(Dialect dialect) { + return new DatabaseConnectionInfoImpl( + DriverManagerConnectionProvider.class, + dbInfo.getJdbcUrl(), + dbInfo.getJdbcDriver(), + dialect.getClass(), + dialect.getVersion(), + dbInfo.hasSchema(), + dbInfo.hasCatalog(), + dbInfo.getSchema(), + dbInfo.getCatalog(), + dbInfo.getAutoCommitMode(), + dbInfo.getIsolationLevel(), + dbInfo.getPoolMinSize(), + dbInfo.getPoolMaxSize(), + dbInfo.getJdbcFetchSize() + ); + } + + @Override + public boolean isUnwrappableAs(Class unwrapType) { + return unwrapType.isAssignableFrom( DriverManagerConnectionProvider.class ); + } + + @Override + @SuppressWarnings( {"unchecked"}) + public T unwrap(Class unwrapType) { + if ( unwrapType.isAssignableFrom( DriverManagerConnectionProvider.class ) ) { + return (T) this; + } + else { + throw new UnknownUnwrapTypeException( unwrapType ); + } + } + + protected int getOpenConnections() { + return state.getPool().getOpenConnectionCount(); + } + + protected void validateConnectionsReturned() { + final int allocationCount = getOpenConnections(); + if ( allocationCount != 0 ) { + ConnectionInfoLogger.INSTANCE.error( "Connection leak detected: there are " + allocationCount + " unclosed connections"); + } + } + + protected void validateConnections(ConnectionValidator validator) { + state.validateConnections( validator ); + } + + // destroy the pool ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Override + public void stop() { + if ( state != null ) { + state.stop(); + validateConnectionsReturned(); + } + } + + @Override + protected void finalize() throws Throwable { + if ( state != null ) { + state.stop(); + } + super.finalize(); + } + + @Override + public boolean isValid(Connection connection) throws SQLException { + return true; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java index 2450ccecf331..13fa1d73f041 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java @@ -4,369 +4,9 @@ */ package org.hibernate.engine.jdbc.connections.internal; -import java.sql.Connection; -import java.sql.Driver; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.Map; -import java.util.Properties; - -import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; -import org.hibernate.dialect.Database; -import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.SimpleDatabaseVersion; -import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; -import org.hibernate.engine.jdbc.connections.spi.ConnectionProviderConfigurationException; -import org.hibernate.engine.jdbc.connections.spi.DatabaseConnectionInfo; -import org.hibernate.exception.JDBCConnectionException; -import org.hibernate.service.ServiceRegistry; -import org.hibernate.service.UnknownUnwrapTypeException; -import org.hibernate.service.spi.Configurable; -import org.hibernate.service.spi.ServiceException; -import org.hibernate.service.spi.ServiceRegistryAwareService; -import org.hibernate.service.spi.ServiceRegistryImplementor; -import org.hibernate.service.spi.Stoppable; -import org.hibernate.internal.log.ConnectionInfoLogger; - -import static org.hibernate.cfg.JdbcSettings.AUTOCOMMIT; -import static org.hibernate.cfg.JdbcSettings.DRIVER; -import static org.hibernate.cfg.JdbcSettings.JAKARTA_JDBC_URL; -import static org.hibernate.cfg.JdbcSettings.POOL_SIZE; -import static org.hibernate.cfg.JdbcSettings.URL; -import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.extractIsolation; -import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.getConnectionProperties; -import static org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator.toIsolationNiceName; -import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getCatalog; -import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getDriverName; -import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getFetchSize; -import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getIsolation; -import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.getSchema; -import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.hasCatalog; -import static org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl.hasSchema; -import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean; -import static org.hibernate.internal.util.config.ConfigurationHelper.getInt; -import static org.hibernate.internal.util.config.ConfigurationHelper.getLong; -import static org.hibernate.internal.util.config.ConfigurationHelper.getString; - /** - * A connection provider that uses the {@link DriverManager} directly to open connections and provides - * a very rudimentary connection pool. - * - * @implNote Not intended for use in production systems! - * - * @author Gavin King - * @author Steve Ebersole + * @deprecated Use {@link DriverManagerConnectionProvider} */ -public class DriverManagerConnectionProviderImpl - implements ConnectionProvider, Configurable, Stoppable, ServiceRegistryAwareService, ConnectionValidator { - - public static final String MIN_SIZE = "hibernate.connection.min_pool_size"; - public static final String INITIAL_SIZE = "hibernate.connection.initial_pool_size"; - // in TimeUnit.SECONDS - public static final String VALIDATION_INTERVAL = "hibernate.connection.pool_validation_interval"; - public static final String INIT_SQL ="hibernate.connection.init_sql"; - public static final String CONNECTION_CREATOR_FACTORY ="hibernate.connection.creator_factory_class"; - - private volatile PoolState state; - - private static DatabaseConnectionInfo dbInfo; - - // create the pool ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - private volatile ServiceRegistry serviceRegistry; - - @Override - public void injectServices(ServiceRegistryImplementor serviceRegistry) { - this.serviceRegistry = serviceRegistry; - } - - @Override - public void configure(Map configurationValues) { - ConnectionInfoLogger.INSTANCE.usingHibernateBuiltInConnectionPool(); - final PooledConnections pool = buildPool( configurationValues, serviceRegistry ); - final long validationInterval = getLong( VALIDATION_INTERVAL, configurationValues, 30 ); - state = new PoolState( pool, validationInterval ); - } - - private PooledConnections buildPool(Map configuration, ServiceRegistry serviceRegistry) { - // connection settings - final String url = jdbcUrl( configuration ); - final String driverClassName = getString( DRIVER, configuration ); - final Properties connectionProps = getConnectionProperties( configuration ); - final boolean autoCommit = getBoolean( AUTOCOMMIT, configuration ); // default autocommit to false - final Integer isolation = extractIsolation( configuration ); - final String initSql = getString( INIT_SQL, configuration ); - - // pool settings - final int minSize = getInt( MIN_SIZE, configuration, 1 ); - final int maxSize = getInt( POOL_SIZE, configuration, 20 ); - final int initialSize = getInt( INITIAL_SIZE, configuration, minSize ); - - final Driver driver = loadDriver( driverClassName, serviceRegistry, url ); - if ( driver == null ) { - //we're hoping that the driver is already loaded - logAvailableDrivers(); - } - - final var connectionCreator = - getConnectionCreatorFactory( configuration, serviceRegistry ) - .create( - driver, - serviceRegistry, - url, - connectionProps, - autoCommit, - isolation, - initSql, - configuration - ); - - try ( var connection = connectionCreator.createConnection() ) { - dbInfo = new DatabaseConnectionInfoImpl( - DriverManagerConnectionProviderImpl.class, - url, - getDriverName( connection ), - null, - SimpleDatabaseVersion.ZERO_VERSION, - hasSchema( connection ), - hasCatalog( connection ), - getSchema( connection ), - getCatalog( connection ), - Boolean.toString( autoCommit ), - isolation != null - ? toIsolationNiceName( isolation ) - : toIsolationNiceName( getIsolation( connection ) ), - minSize, - maxSize, - getFetchSize( connection ) - ); - if ( !connection.getAutoCommit() ) { - connection.rollback(); - } - } - catch (SQLException e) { - throw new JDBCConnectionException( "Could not create connection", e ); - } - - return new PooledConnections.Builder( connectionCreator ) - .autoCommit( autoCommit ) - .initialSize( initialSize ) - .minSize( minSize ) - .maxSize( maxSize ) - .validator( this ) - .build(); - } - - private static Driver loadDriver(String driverClassName, ServiceRegistry serviceRegistry, String url) { - if ( driverClassName != null ) { - return loadDriverIfPossible( driverClassName, serviceRegistry ); - } - else { - // try to guess the driver class from the JDBC URL - for ( var database: Database.values() ) { - if ( database.matchesUrl( url ) ) { - final String databaseDriverClassName = database.getDriverClassName( url ); - if ( databaseDriverClassName != null ) { - try { - return loadDriverIfPossible( databaseDriverClassName, serviceRegistry ); - } - catch (Exception e) { - // swallow it, since this was not an explicit setting by the user - } - } - } - } - return null; - } - } - - private static void logAvailableDrivers() { - ConnectionInfoLogger.INSTANCE.jdbcDriverNotSpecified(); - final var list = new StringBuilder(); - DriverManager.drivers() - .forEach( driver -> { - if ( !list.isEmpty() ) { - list.append( ", " ); - } - list.append( driver.getClass().getName() ); - } ); - ConnectionInfoLogger.INSTANCE.availableJdbcDrivers( list.toString() ); - } - - private static String jdbcUrl(Map configuration) { - final String url = (String) configuration.get( URL ); - if ( url == null ) { - throw new ConnectionProviderConfigurationException( "No JDBC URL specified by property '" + JAKARTA_JDBC_URL + "'" ); - } - return url; - } - - private static ConnectionCreatorFactory getConnectionCreatorFactory( - Map configuration, ServiceRegistry serviceRegistry) { - final Object connectionCreatorFactory = configuration.get( CONNECTION_CREATOR_FACTORY ); - final ConnectionCreatorFactory factory; - if ( connectionCreatorFactory instanceof ConnectionCreatorFactory instance ) { - factory = instance; - } - else if ( connectionCreatorFactory != null ) { - factory = loadConnectionCreatorFactory( connectionCreatorFactory.toString(), serviceRegistry ); - } - else { - factory = null; - } - return factory == null ? ConnectionCreatorFactoryImpl.INSTANCE : factory; - } - - private static Driver loadDriverIfPossible(String driverClassName, ServiceRegistry serviceRegistry) { - if ( driverClassName == null ) { - ConnectionInfoLogger.INSTANCE.debug( "No driver class specified" ); - return null; - } - else if ( serviceRegistry != null ) { - final Class driverClass = - serviceRegistry.requireService( ClassLoaderService.class ) - .classForName( driverClassName ); - try { - return driverClass.newInstance(); - } - catch ( Exception e ) { - throw new ServiceException( "Specified JDBC Driver " + driverClassName + " could not be loaded", e ); - } - } - else { - try { - return (Driver) Class.forName( driverClassName ).newInstance(); - } - catch (Exception e1) { - throw new ServiceException( "Specified JDBC Driver " + driverClassName + " could not be loaded", e1 ); - } - } - } - - private static ConnectionCreatorFactory loadConnectionCreatorFactory( - String connectionCreatorFactoryClassName, ServiceRegistry serviceRegistry) { - if ( connectionCreatorFactoryClassName == null ) { - ConnectionInfoLogger.INSTANCE.debug( "No connection creator factory class specified" ); - return null; - } - else if ( serviceRegistry != null ) { - final Class factoryClass = - serviceRegistry.requireService( ClassLoaderService.class ) - .classForName( connectionCreatorFactoryClassName ); - try { - return factoryClass.newInstance(); - } - catch ( Exception e ) { - throw new ServiceException( "Specified ConnectionCreatorFactory " + connectionCreatorFactoryClassName - + " could not be loaded", e ); - } - } - else { - try { - return (ConnectionCreatorFactory) Class.forName( connectionCreatorFactoryClassName ).newInstance(); - } - catch (Exception e1) { - throw new ServiceException( "Specified ConnectionCreatorFactory " + connectionCreatorFactoryClassName - + " could not be loaded", e1 ); - } - } - } - - - // use the pool ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - @Override - public Connection getConnection() throws SQLException { - if ( state == null ) { - throw new IllegalStateException( "Cannot get a connection as the driver manager is not properly initialized" ); - } - return state.getConnection(); - } - - @Override - public void closeConnection(Connection connection) throws SQLException { - if ( state == null ) { - throw new IllegalStateException( "Cannot close a connection as the driver manager is not properly initialized" ); - } - state.closeConnection( connection ); - } - - @Override - public boolean supportsAggressiveRelease() { - return false; - } - - @Override - public DatabaseConnectionInfo getDatabaseConnectionInfo(Dialect dialect) { - return new DatabaseConnectionInfoImpl( - DriverManagerConnectionProviderImpl.class, - dbInfo.getJdbcUrl(), - dbInfo.getJdbcDriver(), - dialect.getClass(), - dialect.getVersion(), - dbInfo.hasSchema(), - dbInfo.hasCatalog(), - dbInfo.getSchema(), - dbInfo.getCatalog(), - dbInfo.getAutoCommitMode(), - dbInfo.getIsolationLevel(), - dbInfo.getPoolMinSize(), - dbInfo.getPoolMaxSize(), - dbInfo.getJdbcFetchSize() - ); - } - - @Override - public boolean isUnwrappableAs(Class unwrapType) { - return unwrapType.isAssignableFrom( DriverManagerConnectionProviderImpl.class ); - } - - @Override - @SuppressWarnings( {"unchecked"}) - public T unwrap(Class unwrapType) { - if ( unwrapType.isAssignableFrom( DriverManagerConnectionProviderImpl.class ) ) { - return (T) this; - } - else { - throw new UnknownUnwrapTypeException( unwrapType ); - } - } - - protected int getOpenConnections() { - return state.getPool().getOpenConnectionCount(); - } - - protected void validateConnectionsReturned() { - final int allocationCount = getOpenConnections(); - if ( allocationCount != 0 ) { - ConnectionInfoLogger.INSTANCE.error( "Connection leak detected: there are " + allocationCount + " unclosed connections"); - } - } - - protected void validateConnections(ConnectionValidator validator) { - state.validateConnections( validator ); - } - - // destroy the pool ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - @Override - public void stop() { - if ( state != null ) { - state.stop(); - validateConnectionsReturned(); - } - } - - @Override - protected void finalize() throws Throwable { - if ( state != null ) { - state.stop(); - } - super.finalize(); - } - - @Override - public boolean isValid(Connection connection) throws SQLException { - return true; - } +@Deprecated(since = "7.1", forRemoval = true) +public class DriverManagerConnectionProviderImpl extends DriverManagerConnectionProvider { } diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java index 28262cb322aa..a99ed62e830c 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java @@ -330,8 +330,10 @@ public void registerExportables(Database database) { table.setOptions( options ); - table.addInitCommand( context -> new InitCommand( "insert into " - + context.format( physicalTableName ) + " values ( " + initialValue + " )" ) ); + table.addInitCommand( context -> new InitCommand( + "insert into " + context.format( physicalTableName ) + + " ( " + valueColumnNameText + " ) values ( " + initialValue + " )" + ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/EmbeddableInstantiator.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/EmbeddableInstantiator.java index 21bb714a09b3..17c82f0193d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/EmbeddableInstantiator.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/EmbeddableInstantiator.java @@ -10,7 +10,7 @@ * Contract for instantiating embeddable values. * * @apiNote Incubating until the proposed - * {@code instantiate(IntFunction valueAccess, SessionFactoryImplementor sessionFactory)} + * {@code instantiate(IntFunction valueAccess)} * form can be implemented. * * @see org.hibernate.annotations.EmbeddableInstantiator 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 51f5a729fe6c..dd66e3708035 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 @@ -1174,7 +1174,14 @@ public NativeQueryImplementor addScalar(String columnAlias, @SuppressWarnings @Override public NativeQueryImplementor addScalar(String columnAlias, @SuppressWarnings("rawtypes") Class javaType) { - return registerBuilder( Builders.scalar( columnAlias, javaType, getSessionFactory() ) ); + @SuppressWarnings("unchecked") + final BasicType basicType = getBasicTypeRegistry().getRegisteredType( javaType ); + if ( basicType != null ) { + return registerBuilder( Builders.scalar( columnAlias, basicType ) ); + } + else { + return registerBuilder( Builders.scalar( columnAlias, javaType, getSessionFactory() ) ); + } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/Template.java b/hibernate-core/src/main/java/org/hibernate/sql/Template.java index 1d97702b483a..08c00f1eb898 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/Template.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/Template.java @@ -78,7 +78,8 @@ public final class Template { "minus", "except", "intersect", - "partition"); + "partition", + "within"); private static final Set BEFORE_TABLE_KEYWORDS = Set.of("from", "join"); private static final Set FUNCTION_KEYWORDS @@ -93,6 +94,13 @@ public final class Template { = Set.of("first", "next"); private static final Set CURRENT_BIGRAMS = Set.of("date", "time", "timestamp"); + // Ordered-set aggregate function names we want to recognize + private static final Set ORDERED_SET_AGGREGATES + = Set.of("listagg", "percentile_cont", "percentile_disc", "mode"); + // Soft keywords that are only treated as keywords in the LISTAGG extension immediately + // following the argument list and up to and including GROUP + private static final Set LISTAGG_EXTENSION_KEYWORDS + = Set.of("on", "overflow", "error", "truncate", "without", "count", "within", "with", "group"); private static final String PUNCTUATION = "=> parsed = configuration.getXmlMappingBinderAccess().bind( xmlStream ); + Binding parsed = InputStreamXmlSource.fromStream( xmlStream, configuration.getXmlMappingBinderAccess().getMappingBinder() ); configuration.addXmlMapping( parsed ); } catch (IOException e) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitInfoTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitInfoTests.java index 5f206c4695ef..4d1133b43ac2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitInfoTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitInfoTests.java @@ -12,7 +12,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.H2Dialect; -import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DataSourceConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.jpa.HibernatePersistenceProvider; @@ -58,8 +58,8 @@ public DataSource getNonJtaDataSource() { final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) .getServiceRegistry() .getService( ConnectionProvider.class ); - assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); - final DatasourceConnectionProviderImpl dsCp = (DatasourceConnectionProviderImpl) connectionProvider; + assertThat( connectionProvider, instanceOf( DataSourceConnectionProvider.class ) ); + final DataSourceConnectionProvider dsCp = (DataSourceConnectionProvider) connectionProvider; assertThat( dsCp.getDataSource(), is( puDataSource ) ); // now let's check that it is exposed via the EMF properties @@ -92,8 +92,8 @@ public DataSource getJtaDataSource() { final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) .getServiceRegistry() .getService( ConnectionProvider.class ); - assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); - final DatasourceConnectionProviderImpl dsCp = (DatasourceConnectionProviderImpl) connectionProvider; + assertThat( connectionProvider, instanceOf( DataSourceConnectionProvider.class ) ); + final DataSourceConnectionProvider dsCp = (DataSourceConnectionProvider) connectionProvider; assertThat( dsCp.getDataSource(), is( puDataSource ) ); // now let's check that it is exposed via the EMF properties diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitOverridesTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitOverridesTests.java index 904404f3bce1..00abd23f5ae4 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitOverridesTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitOverridesTests.java @@ -16,8 +16,8 @@ import org.hibernate.dialect.Dialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.SimpleDatabaseVersion; -import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DataSourceConnectionProvider; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.jpa.HibernatePersistenceProvider; @@ -106,8 +106,8 @@ public void testPassingIntegrationJtaDataSourceOverrideForJpaJdbcSettings() { final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) .getServiceRegistry() .getService( ConnectionProvider.class ); - assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); - final DatasourceConnectionProviderImpl dsCp = (DatasourceConnectionProviderImpl) connectionProvider; + assertThat( connectionProvider, instanceOf( DataSourceConnectionProvider.class ) ); + final DataSourceConnectionProvider dsCp = (DataSourceConnectionProvider) connectionProvider; assertThat( dsCp.getDataSource(), is( integrationDataSource ) ); // now let's check that it is exposed via the EMF properties @@ -190,7 +190,7 @@ public Properties getProperties() { final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) .getServiceRegistry() .getService( ConnectionProvider.class ); - assertThat( connectionProvider, instanceOf( DriverManagerConnectionProviderImpl.class ) ); + assertThat( connectionProvider, instanceOf( DriverManagerConnectionProvider.class ) ); } finally { emf.close(); @@ -243,7 +243,7 @@ public DataSource getJtaDataSource() { final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) .getServiceRegistry() .getService( ConnectionProvider.class ); - assertThat( connectionProvider, instanceOf( DriverManagerConnectionProviderImpl.class ) ); + assertThat( connectionProvider, instanceOf( DriverManagerConnectionProvider.class ) ); } finally { emf.close(); @@ -298,9 +298,9 @@ public DataSource getJtaDataSource() { final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) .getServiceRegistry() .getService( ConnectionProvider.class ); - assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); + assertThat( connectionProvider, instanceOf( DataSourceConnectionProvider.class ) ); - final DatasourceConnectionProviderImpl datasourceConnectionProvider = (DatasourceConnectionProviderImpl) connectionProvider; + final DataSourceConnectionProvider datasourceConnectionProvider = (DataSourceConnectionProvider) connectionProvider; assertThat( datasourceConnectionProvider.getDataSource(), is( integrationDataSource ) ); } finally { @@ -345,9 +345,9 @@ public DataSource getNonJtaDataSource() { final SessionFactoryImplementor sessionFactory = emf.unwrap( SessionFactoryImplementor.class ); final ConnectionProvider connectionProvider = sessionFactory.getServiceRegistry().getService( ConnectionProvider.class ); - assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); + assertThat( connectionProvider, instanceOf( DataSourceConnectionProvider.class ) ); - final DatasourceConnectionProviderImpl dsProvider = (DatasourceConnectionProviderImpl) connectionProvider; + final DataSourceConnectionProvider dsProvider = (DataSourceConnectionProvider) connectionProvider; assertThat( dsProvider.getDataSource(), is( override ) ); } finally { @@ -374,7 +374,7 @@ public DataSource getNonJtaDataSource() { integrationSettings.put( AvailableSettings.JPA_JDBC_URL, ConnectionProviderBuilder.URL ); integrationSettings.put( AvailableSettings.JPA_JDBC_USER, ConnectionProviderBuilder.USER ); integrationSettings.put( AvailableSettings.JPA_JDBC_PASSWORD, ConnectionProviderBuilder.PASS ); - integrationSettings.put( DriverManagerConnectionProviderImpl.INIT_SQL, "" ); + integrationSettings.put( DriverManagerConnectionProvider.INIT_SQL, "" ); final PersistenceProvider provider = new HibernatePersistenceProvider(); @@ -387,7 +387,7 @@ public DataSource getNonJtaDataSource() { final SessionFactoryImplementor sessionFactory = emf.unwrap( SessionFactoryImplementor.class ); final ConnectionProvider connectionProvider = sessionFactory.getServiceRegistry().getService( ConnectionProvider.class ); - assertThat( connectionProvider, instanceOf( DriverManagerConnectionProviderImpl.class ) ); + assertThat( connectionProvider, instanceOf( DriverManagerConnectionProvider.class ) ); } finally { emf.close(); @@ -413,7 +413,7 @@ public DataSource getNonJtaDataSource() { integrationSettings.put( AvailableSettings.JAKARTA_JDBC_URL, ConnectionProviderBuilder.URL ); integrationSettings.put( AvailableSettings.JAKARTA_JDBC_USER, ConnectionProviderBuilder.USER ); integrationSettings.put( AvailableSettings.JAKARTA_JDBC_PASSWORD, ConnectionProviderBuilder.PASS ); - integrationSettings.put( DriverManagerConnectionProviderImpl.INIT_SQL, "" ); + integrationSettings.put( DriverManagerConnectionProvider.INIT_SQL, "" ); final PersistenceProvider provider = new HibernatePersistenceProvider(); @@ -426,7 +426,7 @@ public DataSource getNonJtaDataSource() { final SessionFactoryImplementor sessionFactory = emf.unwrap( SessionFactoryImplementor.class ); final ConnectionProvider connectionProvider = sessionFactory.getServiceRegistry().getService( ConnectionProvider.class ); - assertThat( connectionProvider, instanceOf( DriverManagerConnectionProviderImpl.class ) ); + assertThat( connectionProvider, instanceOf( DriverManagerConnectionProvider.class ) ); } finally { emf.close(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/ScanningCoordinatorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/ScanningCoordinatorTest.java index 2e5b4d75c92c..96282cf8fd79 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/ScanningCoordinatorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/scanning/ScanningCoordinatorTest.java @@ -23,6 +23,7 @@ import org.hibernate.boot.archive.scan.spi.ScanResult; import org.hibernate.boot.archive.scan.spi.Scanner; import org.hibernate.boot.archive.spi.InputStreamAccess; +import org.hibernate.boot.jaxb.internal.MappingBinder; import org.hibernate.boot.spi.ClassmateContext; import org.hibernate.boot.model.convert.spi.ConverterDescriptor; import org.hibernate.boot.model.process.internal.ManagedResourcesImpl; @@ -69,6 +70,7 @@ public class ScanningCoordinatorTest { private BootstrapContext bootstrapContext = Mockito.mock( BootstrapContext.class ); private ClassmateContext classmateContext = new ClassmateContext(); private XmlMappingBinderAccess xmlMappingBinderAccess = Mockito.mock( XmlMappingBinderAccess.class ); + private MappingBinder mappingBinder = Mockito.mock( MappingBinder.class ); private MetadataBuildingOptions metadataBuildingOptions = Mockito.mock( MetadataBuildingOptions.class ); private ScanEnvironment scanEnvironment = Mockito.mock( ScanEnvironment.class ); @@ -92,6 +94,8 @@ public void init() { when( serviceRegistry.requireService( ClassLoaderService.class ) ).thenReturn( classLoaderService ); when( bootstrapContext.getClassLoaderService() ).thenReturn( classLoaderService ); + when( xmlMappingBinderAccess.getMappingBinder() ).thenReturn( mappingBinder ); + when( metadataBuildingOptions.isXmlMappingEnabled() ).thenReturn( true ); when( scanEnvironment.getExplicitlyListedClassNames() ).thenReturn( List.of( "a.b.C" ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cache/SingleRegisteredProviderTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cache/SingleRegisteredProviderTest.java index 1657b083db24..bea4b7d4f332 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/cache/SingleRegisteredProviderTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cache/SingleRegisteredProviderTest.java @@ -13,7 +13,7 @@ import org.hibernate.cache.internal.NoCachingRegionFactory; import org.hibernate.cache.spi.RegionFactory; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.junit4.BaseUnitTestCase; @@ -91,14 +91,14 @@ public void testConnectionsRegistered() { bsr.getService( StrategySelector.class ).registerStrategyImplementor( ConnectionProvider.class, "testing", - DriverManagerConnectionProviderImpl.class + DriverManagerConnectionProvider.class ); final StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistryBuilder( bsr ).build(); final ConnectionProvider configuredProvider = ssr.getService( ConnectionProvider.class ); - assertThat( configuredProvider, instanceOf( DriverManagerConnectionProviderImpl.class ) ); + assertThat( configuredProvider, instanceOf( DriverManagerConnectionProvider.class ) ); } } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/connection/DriverManagerConnectionProviderTransactionIsolationConfigTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/connection/DriverManagerConnectionProviderTransactionIsolationConfigTest.java index 99f4b7198225..2526bf217593 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/connection/DriverManagerConnectionProviderTransactionIsolationConfigTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/connection/DriverManagerConnectionProviderTransactionIsolationConfigTest.java @@ -4,7 +4,7 @@ */ package org.hibernate.orm.test.connection; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.common.connections.BaseTransactionIsolationConfigTest; @@ -21,6 +21,6 @@ public class DriverManagerConnectionProviderTransactionIsolationConfigTest extends BaseTransactionIsolationConfigTest { @Override protected ConnectionProvider getConnectionProviderUnderTest() { - return new DriverManagerConnectionProviderImpl(); + return new DriverManagerConnectionProvider(); } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/connection/DriverManagerConnectionProviderValidationConfigTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/connection/DriverManagerConnectionProviderValidationConfigTest.java index 89a3c393ce54..df8d7b4d8db5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/connection/DriverManagerConnectionProviderValidationConfigTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/connection/DriverManagerConnectionProviderValidationConfigTest.java @@ -9,7 +9,7 @@ import jakarta.persistence.Id; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.Jpa; @@ -27,7 +27,7 @@ integrationSettings = { // Force a non-shared connection provider to avoid re-creation of the shared pool @Setting(name = AvailableSettings.CONNECTION_PROVIDER, value = ""), - @Setting(name = DriverManagerConnectionProviderImpl.VALIDATION_INTERVAL, value = "1") + @Setting(name = DriverManagerConnectionProvider.VALIDATION_INTERVAL, value = "1") } ) public class DriverManagerConnectionProviderValidationConfigTest { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/datasource/TestDataSourceConnectionProvider.java b/hibernate-core/src/test/java/org/hibernate/orm/test/datasource/TestDataSourceConnectionProvider.java index e28d11f7ab47..31c4c59db798 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/datasource/TestDataSourceConnectionProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/datasource/TestDataSourceConnectionProvider.java @@ -4,8 +4,8 @@ */ package org.hibernate.orm.test.datasource; -import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DataSourceConnectionProvider; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.service.spi.ServiceRegistryAwareService; import org.hibernate.service.spi.ServiceRegistryImplementor; @@ -19,10 +19,10 @@ @SuppressWarnings( "unused" ) // used by DatasourceTest in this package public class TestDataSourceConnectionProvider - extends DatasourceConnectionProviderImpl + extends DataSourceConnectionProvider implements ServiceRegistryAwareService { - final DriverManagerConnectionProviderImpl delegate = new DriverManagerConnectionProviderImpl(); + final DriverManagerConnectionProvider delegate = new DriverManagerConnectionProvider(); @Override public void configure(Map configuration) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/AnsiNullTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/AnsiNullTest.java index 050dff360e5a..92e03dbb2f7b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/AnsiNullTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/AnsiNullTest.java @@ -7,7 +7,7 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; import org.hibernate.dialect.SybaseASEDialect; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.orm.test.length.WithLongStrings; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.Jira; @@ -65,7 +65,7 @@ void tearDown(SessionFactoryScope scope) { @RequiresDialect(value = SybaseASEDialect.class) @DomainModel(annotatedClasses = { AnsiNullTest.Book.class, WithLongStrings.class }) @SessionFactory - @ServiceRegistry( settings = {@Setting(name = DriverManagerConnectionProviderImpl.INIT_SQL, value = "set ansinull on")} ) + @ServiceRegistry( settings = {@Setting(name = DriverManagerConnectionProvider.INIT_SQL, value = "set ansinull on")} ) public void testWithAnsiNullOn(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -109,7 +109,7 @@ public void testWithAnsiNullOn(SessionFactoryScope scope) { @RequiresDialect(value = SybaseASEDialect.class) @DomainModel(annotatedClasses = { AnsiNullTest.Book.class, WithLongStrings.class }) @SessionFactory - @ServiceRegistry( settings = {@Setting(name = DriverManagerConnectionProviderImpl.INIT_SQL, value = "set ansinull on")} ) + @ServiceRegistry( settings = {@Setting(name = DriverManagerConnectionProvider.INIT_SQL, value = "set ansinull on")} ) public void testLOBWithAnsiNullOn(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -152,7 +152,7 @@ public void testLOBWithAnsiNullOn(SessionFactoryScope scope) { @RequiresDialect(value = SybaseASEDialect.class) @DomainModel(annotatedClasses = { AnsiNullTest.Book.class, WithLongStrings.class }) @SessionFactory - @ServiceRegistry( settings = {@Setting(name = DriverManagerConnectionProviderImpl.INIT_SQL, value = "set ansinull off")} ) + @ServiceRegistry( settings = {@Setting(name = DriverManagerConnectionProvider.INIT_SQL, value = "set ansinull off")} ) public void testWithAnsiNullOff(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -195,7 +195,7 @@ public void testWithAnsiNullOff(SessionFactoryScope scope) { @RequiresDialect(value = SybaseASEDialect.class) @DomainModel(annotatedClasses = { AnsiNullTest.Book.class, WithLongStrings.class }) @SessionFactory - @ServiceRegistry( settings = {@Setting(name = DriverManagerConnectionProviderImpl.INIT_SQL, value = "set ansinull off")} ) + @ServiceRegistry( settings = {@Setting(name = DriverManagerConnectionProvider.INIT_SQL, value = "set ansinull off")} ) public void testLOBWithAnsiNullOff(SessionFactoryScope scope) { scope.inTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SQLServerDialectTempTableCollationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SQLServerDialectTempTableCollationTest.java index e66b2b20d251..a351e0d007e7 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SQLServerDialectTempTableCollationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SQLServerDialectTempTableCollationTest.java @@ -21,7 +21,7 @@ import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.orm.junit.JiraKey; -import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProvider; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.transaction.TransactionUtil; import org.junit.Assert; @@ -83,7 +83,7 @@ protected void releaseSessionFactory() { } } // The alter database calls could lead to issues with existing connections, so we reset the shared pool here - SharedDriverManagerConnectionProviderImpl.getInstance().reset(); + SharedDriverManagerConnectionProvider.getInstance().reset(); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SequenceInformationMariaDBTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SequenceInformationMariaDBTest.java index a4db1dca655c..7a6f4ae7b712 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SequenceInformationMariaDBTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SequenceInformationMariaDBTest.java @@ -18,7 +18,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; import org.hibernate.dialect.MariaDBDialect; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.internal.util.PropertiesHelper; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; @@ -48,11 +48,11 @@ ) public class SequenceInformationMariaDBTest { - private DriverManagerConnectionProviderImpl connectionProvider; + private DriverManagerConnectionProvider connectionProvider; @BeforeAll public void init() { - connectionProvider = new DriverManagerConnectionProviderImpl(); + connectionProvider = new DriverManagerConnectionProvider(); connectionProvider.configure( PropertiesHelper.map( Environment.getProperties() ) ); try(Connection connection = connectionProvider.getConnection(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/sequence/PooledWithCustomNamingStrategyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/sequence/PooledWithCustomNamingStrategyTest.java index 9fbc0a918c98..c9ecf985863b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/id/sequence/PooledWithCustomNamingStrategyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/sequence/PooledWithCustomNamingStrategyTest.java @@ -17,7 +17,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; import org.hibernate.dialect.H2Dialect; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -91,7 +91,7 @@ public void testRightIncrementSize() { @BeforeAll public void setUp() { - final DriverManagerConnectionProviderImpl provider = new DriverManagerConnectionProviderImpl(); + final DriverManagerConnectionProvider provider = new DriverManagerConnectionProvider(); provider.configure( PropertiesHelper.map( Environment.getProperties() ) ); connectionProvider = provider; try (final Connection connection = connectionProvider.getConnection(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/AddScalarTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/AddScalarTest.java new file mode 100644 index 000000000000..fe01c20d9f4a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/usertype/AddScalarTest.java @@ -0,0 +1,132 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.id.usertype; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import org.hibernate.boot.model.TypeContributions; +import org.hibernate.boot.model.TypeContributor; +import org.hibernate.query.NativeQuery; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.usertype.UserType; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +@DomainModel( + annotatedClasses = AddScalarTest.Book.class, + typeContributors = AddScalarTest.UuidTypeContributor.class +) +@SessionFactory +@JiraKey(value = "HHH-19703") +public class AddScalarTest { + + @BeforeAll + static void init(SessionFactoryScope scope) { + scope.inTransaction( session -> + session.persist( new Book( 1L, UUID.randomUUID().toString() ) ) ); + } + + @AfterAll + static void clean(SessionFactoryScope scope) { + scope.inTransaction( session -> + session.createMutationQuery( "delete from Book" ).executeUpdate() ); + } + + @Test + public void test(SessionFactoryScope scope) { + final var actual = scope.fromSession( session -> + session.createNativeQuery( "select uuid from book where id=:id" ) + .setParameter( "id", Long.valueOf( 1 ) ) + .unwrap( NativeQuery.class ) + .addScalar( "uuid", Uuid.class ) + .getSingleResult() ); + assertInstanceOf( Uuid.class, actual ); + } + + @Entity(name = "Book") + @Table(name = "book") + static class Book { + + @Id + private Long id; + + private String uuid; + + public Book() { + } + + public Book(Long id, String uuid) { + this.id = id; + this.uuid = uuid; + } + } + + record Uuid(UUID uuid) { + + } + + static class UuidType implements UserType { + + @Override + public int getSqlType() { + return SqlTypes.VARCHAR; + } + + @Override + public Class returnedClass() { + return Uuid.class; + } + + @Override + public Uuid deepCopy(Uuid value) { + return new Uuid( value.uuid ); + } + + @Override + public boolean isMutable() { + return false; + } + + @Override + public Uuid nullSafeGet(ResultSet rs, int position, WrapperOptions options) throws SQLException { + final var result = rs.getString( position ); + return rs.wasNull() ? null : new Uuid( UUID.fromString( result ) ); + } + + @Override + public void nullSafeSet(PreparedStatement st, Uuid value, int position, WrapperOptions options) + throws SQLException { + if ( value == null ) { + st.setNull( position, getSqlType() ); + } + else { + st.setObject( position, value.uuid.toString(), getSqlType() ); + } + } + } + + static class UuidTypeContributor implements TypeContributor { + + @Override + public void contribute(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { + typeContributions.contributeType( new UuidType() ); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/jakarta/JakartaSchemaToolingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/jakarta/JakartaSchemaToolingTests.java index 6d0fa83c5699..d3c64b8f8f41 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/jakarta/JakartaSchemaToolingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/jakarta/JakartaSchemaToolingTests.java @@ -11,7 +11,7 @@ import org.hibernate.SessionFactoryObserver; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.jpa.boot.spi.Bootstrap; import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder; @@ -147,10 +147,10 @@ private SessionFactoryImplementor buildSessionFactory(Object... settingPairs) { final Properties settings = new Properties(); settings.setProperty( AvailableSettings.AUTOCOMMIT, "false" ); settings.setProperty( AvailableSettings.POOL_SIZE, "5" ); - settings.setProperty( DriverManagerConnectionProviderImpl.INITIAL_SIZE, "0" ); + settings.setProperty( DriverManagerConnectionProvider.INITIAL_SIZE, "0" ); settings.setProperty( - DriverManagerConnectionProviderImpl.INIT_SQL, - Environment.getProperties().getProperty( DriverManagerConnectionProviderImpl.INIT_SQL ) + DriverManagerConnectionProvider.INIT_SQL, + Environment.getProperties().getProperty( DriverManagerConnectionProvider.INIT_SQL ) ); applyToProperties( settings, settingPairs ); ServiceRegistryUtil.applySettings( settings ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/transaction/TransactionCommitFailureTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/transaction/TransactionCommitFailureTest.java index 810c35d1a8eb..d52f72ab2925 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/transaction/TransactionCommitFailureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/transaction/TransactionCommitFailureTest.java @@ -19,7 +19,7 @@ import org.hibernate.jpa.boot.spi.Bootstrap; import org.hibernate.testing.jdbc.ConnectionProviderDelegate; -import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProvider; import org.hibernate.testing.orm.jpa.PersistenceUnitDescriptorAdapter; import org.hibernate.orm.test.jpa.SettingsGenerator; @@ -123,7 +123,7 @@ private Map basicSettings() { public static class ProxyConnectionProvider extends ConnectionProviderDelegate { public ProxyConnectionProvider() { - setConnectionProvider( SharedDriverManagerConnectionProviderImpl.getInstance() ); + setConnectionProvider( SharedDriverManagerConnectionProvider.getInstance() ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/AbstractMultiTenancyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/AbstractMultiTenancyTest.java index 5c3986492951..1734e80f4660 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/AbstractMultiTenancyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/AbstractMultiTenancyTest.java @@ -23,7 +23,7 @@ import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.internal.util.PropertiesHelper; import org.hibernate.query.Query; @@ -102,8 +102,8 @@ protected void registerConnectionProvider(String tenantIdentifier) { properties.put(Environment.URL, tenantUrl(properties.getProperty(Environment.URL), tenantIdentifier)); - DriverManagerConnectionProviderImpl connectionProvider = - new DriverManagerConnectionProviderImpl(); + DriverManagerConnectionProvider connectionProvider = + new DriverManagerConnectionProvider(); connectionProvider.configure( PropertiesHelper.map(properties) ); connectionProviderMap.put(tenantIdentifier, connectionProvider); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/DatabaseTimeZoneMultiTenancyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/DatabaseTimeZoneMultiTenancyTest.java index 79c8db9e9905..4ecf1303b11f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/DatabaseTimeZoneMultiTenancyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/multitenancy/DatabaseTimeZoneMultiTenancyTest.java @@ -29,7 +29,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; import org.hibernate.dialect.H2Dialect; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.internal.util.PropertiesHelper; import org.hibernate.service.spi.ServiceRegistryImplementor; @@ -104,8 +104,8 @@ protected void registerConnectionProvider(String tenantIdentifier, TimeZone time tenantUrl( properties.getProperty(Environment.URL), tenantIdentifier ) ); - DriverManagerConnectionProviderImpl connectionProvider = - new DriverManagerConnectionProviderImpl(); + DriverManagerConnectionProvider connectionProvider = + new DriverManagerConnectionProvider(); connectionProvider.configure( PropertiesHelper.map(properties) ); connectionProviderMap.put(tenantIdentifier, connectionProvider); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/querycache/QueryCacheTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/querycache/QueryCacheTest.java index a4dc2ab2364b..0e201ca828de 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/querycache/QueryCacheTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/querycache/QueryCacheTest.java @@ -28,7 +28,7 @@ import org.hibernate.type.Type; import org.hibernate.testing.jdbc.ConnectionProviderDelegate; -import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProvider; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.ServiceRegistry; @@ -805,7 +805,7 @@ public static class ProxyConnectionProvider extends ConnectionProviderDelegate { private static final ThreadLocal CONNECTION_RETRIEVAL_EXCEPTION_TO_THROW = new ThreadLocal<>(); public ProxyConnectionProvider() { - setConnectionProvider( SharedDriverManagerConnectionProviderImpl.getInstance() ); + setConnectionProvider( SharedDriverManagerConnectionProvider.getInstance() ); } static void runWithConnectionRetrievalFailure(SQLException exceptionToThrow, Runnable runnable) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/ConnectionsReleaseTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/ConnectionsReleaseTest.java index f0b1747ee795..3fbde03955f2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/ConnectionsReleaseTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/ConnectionsReleaseTest.java @@ -20,7 +20,7 @@ import org.hibernate.tool.schema.TargetType; import org.hibernate.testing.orm.junit.JiraKey; -import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProvider; import org.hibernate.testing.junit4.BaseUnitTestCase; import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.util.ServiceRegistryUtil; @@ -40,11 +40,11 @@ public class ConnectionsReleaseTest extends BaseUnitTestCase { private StandardServiceRegistry ssr; private MetadataImplementor metadata; - private SharedDriverManagerConnectionProviderImpl connectionProvider; + private SharedDriverManagerConnectionProvider connectionProvider; @Before public void setUp() { - connectionProvider = SharedDriverManagerConnectionProviderImpl.getInstance(); + connectionProvider = SharedDriverManagerConnectionProvider.getInstance(); ssr = ServiceRegistryUtil.serviceRegistryBuilder() .addService( ConnectionProvider.class, connectionProvider ) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/service/ServiceBootstrappingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/service/ServiceBootstrappingTest.java index 7f0a8ae19695..693140742dc0 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/service/ServiceBootstrappingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/service/ServiceBootstrappingTest.java @@ -10,7 +10,7 @@ import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl; import org.hibernate.cfg.Environment; import org.hibernate.dialect.H2Dialect; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.ConnectionProviderJdbcConnectionAccess; @@ -50,7 +50,7 @@ public void testBasicBuild() throws Exception{ ConnectionProviderJdbcConnectionAccess.class, jdbcServices.getBootstrapJdbcConnectionAccess() ); - assertTrue( connectionAccess.getConnectionProvider().isUnwrappableAs( DriverManagerConnectionProviderImpl.class ) ); + assertTrue( connectionAccess.getConnectionProvider().isUnwrappableAs( DriverManagerConnectionProvider.class ) ); assertFalse( jdbcServices.getSqlStatementLogger().isLogToStdout() ); } finally { @@ -74,7 +74,7 @@ public void testBuildWithLogging() { ConnectionProviderJdbcConnectionAccess.class, jdbcServices.getBootstrapJdbcConnectionAccess() ); - assertTrue( connectionAccess.getConnectionProvider().isUnwrappableAs( DriverManagerConnectionProviderImpl.class ) ); + assertTrue( connectionAccess.getConnectionProvider().isUnwrappableAs( DriverManagerConnectionProvider.class ) ); assertTrue( jdbcServices.getSqlStatementLogger().isLogToStdout() ); } finally { @@ -93,7 +93,7 @@ public void testBuildWithServiceOverride() { ConnectionProviderJdbcConnectionAccess.class, jdbcServices.getBootstrapJdbcConnectionAccess() ); - assertTrue( connectionAccess.getConnectionProvider().isUnwrappableAs( DriverManagerConnectionProviderImpl.class ) ); + assertTrue( connectionAccess.getConnectionProvider().isUnwrappableAs( DriverManagerConnectionProvider.class ) ); } finally { serviceRegistry.destroy(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/TemplateTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/TemplateTest.java index dad99a32a977..f6e8f3f2050f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/TemplateTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/TemplateTest.java @@ -15,6 +15,8 @@ import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.Test; +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertEquals; @SessionFactory @@ -135,6 +137,362 @@ public void testFetchGrammarVariants(SessionFactoryScope scope) { "select {@}." + dialect.quote("`first`") + " from users fetch first 1 row only", factory ); } + @Test + @JiraKey("HHH-19704") + public void testOrderedSetAggregationVariants(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + + assertWhereStringTemplate( + "SELECT LISTAGG(employee_name, ', ') WITHIN GROUP (ORDER BY hire_date) FROM employees", + "SELECT LISTAGG({@}.employee_name, ', ') WITHIN GROUP (ORDER BY {@}.hire_date) FROM employees", + factory ); + + assertWhereStringTemplate( + "SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) FROM employees", + "SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY {@}.salary) FROM employees", + factory ); + + assertWhereStringTemplate( + "SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY salary) FROM employees", + "SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY {@}.salary) FROM employees", + factory ); + + assertWhereStringTemplate( + "SELECT MODE() WITHIN GROUP (ORDER BY job_title) FROM employees", + "SELECT MODE() WITHIN GROUP (ORDER BY {@}.job_title) FROM employees", + factory ); + } + + @Test + @JiraKey("HHH-19704") + public void testOrderedSetAggregationExtendedVariants(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + + assertWhereStringTemplate( + """ + SELECT LISTAGG(employee_name, ', ') ON OVERFLOW ERROR WITHIN GROUP (ORDER BY hire_date) + FROM employees""", + """ + SELECT LISTAGG({@}.employee_name, ', ') ON OVERFLOW ERROR WITHIN GROUP (ORDER BY {@}.hire_date) + FROM employees""", + factory ); + + assertWhereStringTemplate( + """ + SELECT LISTAGG(employee_name, ', ') ON OVERFLOW TRUNCATE WITH COUNT WITHIN GROUP (ORDER BY hire_date) + FROM employees""", + """ + SELECT LISTAGG({@}.employee_name, ', ') ON OVERFLOW TRUNCATE WITH COUNT WITHIN GROUP (ORDER BY {@}.hire_date) + FROM employees""", + factory ); + + assertWhereStringTemplate( + """ + SELECT LISTAGG(employee_name, ', ') ON OVERFLOW TRUNCATE '...' WITH COUNT WITHIN GROUP (ORDER BY hire_date) + FROM employees""", + """ + SELECT LISTAGG({@}.employee_name, ', ') ON OVERFLOW TRUNCATE '...' WITH COUNT WITHIN GROUP (ORDER BY {@}.hire_date) + FROM employees""", + factory ); + + assertWhereStringTemplate( + """ + SELECT LISTAGG(employee_name, ', ') ON OVERFLOW TRUNCATE WITHOUT COUNT WITHIN GROUP (ORDER BY hire_date) + FROM employees""", + """ + SELECT LISTAGG({@}.employee_name, ', ') ON OVERFLOW TRUNCATE WITHOUT COUNT WITHIN GROUP (ORDER BY {@}.hire_date) + FROM employees""", + factory ); + } + + @Test + public void testRenderTransformerReadFragment(SessionFactoryScope scope) { + // Test the renderTransformerReadFragment method + String fragment = "SELECT name, age FROM users WHERE id = ?"; + String result = Template.renderTransformerReadFragment(fragment, "name", "age"); + assertEquals("SELECT {@}.name, {@}.age FROM users WHERE id = ?", result); + + // Test with no column names + result = Template.renderTransformerReadFragment(fragment); + assertEquals(fragment, result); + + // Test with empty column names array + result = Template.renderTransformerReadFragment(fragment, new String[0]); + assertEquals(fragment, result); + + // Test with partial column name matches + result = Template.renderTransformerReadFragment("SELECT name, age, address FROM users", "name"); + assertEquals("SELECT {@}.name, age, address FROM users", result); + } + + @Test + public void testCollectColumnNames(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + + // Test collectColumnNames with SQL that gets processed + List columnNames = Template.collectColumnNames( + "SELECT name, age FROM users WHERE id = ?", + factory.getJdbcServices().getDialect(), + factory.getTypeConfiguration() + ); + assertEquals(3, columnNames.size()); // name, age, and id are unqualified identifiers + + // Test collectColumnNames with template that has qualified identifiers + columnNames = Template.collectColumnNames("SELECT {@}.name, {@}.age FROM users WHERE {@}.id = ?"); + assertEquals(3, columnNames.size()); + assertEquals("name", columnNames.get(0)); + assertEquals("age", columnNames.get(1)); + assertEquals("id", columnNames.get(2)); + + // Test with mixed qualified and unqualified identifiers + columnNames = Template.collectColumnNames("SELECT {@}.name, age, {@}.address FROM users"); + assertEquals(2, columnNames.size()); + assertEquals("name", columnNames.get(0)); + assertEquals("address", columnNames.get(1)); + + // Test with no template placeholders + columnNames = Template.collectColumnNames("SELECT name, age FROM users"); + assertEquals(0, columnNames.size()); + + // Test with template at end of string + columnNames = Template.collectColumnNames("SELECT name FROM users WHERE id = {@}.value"); + assertEquals(1, columnNames.size()); + assertEquals("value", columnNames.get(0)); + } + + @Test + public void testNamedParameters(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + + // Test named parameters are not qualified + assertWhereStringTemplate("SELECT * FROM users WHERE name = :name", + "SELECT * FROM users WHERE {@}.name = :name", factory); + assertWhereStringTemplate("SELECT * FROM users WHERE age > :minAge AND age < :maxAge", + "SELECT * FROM users WHERE {@}.age > :minAge AND {@}.age < :maxAge", factory); + + // Test named parameters mixed with identifiers + assertWhereStringTemplate("SELECT name FROM users WHERE id = :userId AND status = active", + "SELECT {@}.name FROM users WHERE {@}.id = :userId AND {@}.status = {@}.active", factory); + } + + @Test + public void testBooleanLiterals(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + Dialect dialect = factory.getJdbcServices().getDialect(); + + // Test boolean literals are converted to dialect-specific values + assertWhereStringTemplate("SELECT * FROM users WHERE active = true", + "SELECT * FROM users WHERE {@}.active = " + dialect.toBooleanValueString(true), factory); + assertWhereStringTemplate("SELECT * FROM users WHERE deleted = false", + "SELECT * FROM users WHERE {@}.deleted = " + dialect.toBooleanValueString(false), factory); + + // Test boolean literals in expressions + assertWhereStringTemplate("SELECT * FROM users WHERE (active = true) AND (verified = false)", + "SELECT * FROM users WHERE ({@}.active = " + dialect.toBooleanValueString(true) + ") AND ({@}.verified = " + dialect.toBooleanValueString(false) + ")", factory); + + // Test boolean literals mixed with identifiers + assertWhereStringTemplate("SELECT name FROM users WHERE active = true AND status = pending", + "SELECT {@}.name FROM users WHERE {@}.active = " + dialect.toBooleanValueString(true) + " AND {@}.status = {@}.pending", factory); + } + + @Test + public void testCurrentDateTimeExpressions(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + + // Test CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP expressions + assertWhereStringTemplate("SELECT * FROM users WHERE created_date > CURRENT_DATE", + "SELECT * FROM users WHERE {@}.created_date > CURRENT_DATE", factory); + assertWhereStringTemplate("SELECT * FROM logs WHERE timestamp > CURRENT_TIMESTAMP", + "SELECT * FROM logs WHERE timestamp > CURRENT_TIMESTAMP", factory); + assertWhereStringTemplate("SELECT * FROM events WHERE event_time < CURRENT_TIME", + "SELECT * FROM events WHERE {@}.event_time < CURRENT_TIME", factory); + + // Test current expressions in complex queries + assertWhereStringTemplate("SELECT name FROM users WHERE last_login < CURRENT_TIMESTAMP - INTERVAL '1 day'", + "SELECT {@}.name FROM users WHERE {@}.last_login < CURRENT_TIMESTAMP - INTERVAL '1 day'", factory); + } + + @Test + public void testFromClauseHandling(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + + // Test that identifiers in FROM clause are not qualified + assertWhereStringTemplate("SELECT name FROM users WHERE id = 1", + "SELECT {@}.name FROM users WHERE {@}.id = 1", factory); + assertWhereStringTemplate("SELECT u.name, o.order_id FROM users u JOIN orders o ON u.id = o.user_id", + "SELECT u.name, o.order_id FROM users u JOIN orders o ON u.id = o.user_id", factory); + + // Test FROM clause with aliases + assertWhereStringTemplate("SELECT name FROM users AS u WHERE u.id = 1", + "SELECT {@}.name FROM users AS u WHERE u.id = 1", factory); + + // Test complex FROM clause with multiple tables + assertWhereStringTemplate("SELECT p.name, c.name FROM products p, categories c WHERE p.category_id = c.id", + "SELECT p.name, c.name FROM products p, categories c WHERE p.category_id = c.id", factory); + } + + @Test + public void testCastExpressions(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + + // Test CAST expressions with type names + assertWhereStringTemplate("SELECT CAST(price AS DECIMAL(10,2)) FROM products", + "SELECT CAST({@}.price AS DECIMAL(10,2)) FROM products", factory); + assertWhereStringTemplate("SELECT CAST(id AS VARCHAR) FROM users", + "SELECT CAST({@}.id AS VARCHAR) FROM users", factory); + + // Test CAST with complex expressions + assertWhereStringTemplate("SELECT CAST(price * 1.1 AS INTEGER) FROM products", + "SELECT CAST({@}.price * 1.1 AS INTEGER) FROM products", factory); + } + + @Test + public void testFunctionCalls(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + + // Test regular function calls + assertWhereStringTemplate("SELECT UPPER(name) FROM users", + "SELECT UPPER({@}.name) FROM users", factory); + assertWhereStringTemplate("SELECT LOWER(email) FROM users", + "SELECT LOWER({@}.email) FROM users", factory); + assertWhereStringTemplate("SELECT COUNT(*) FROM users", + "SELECT COUNT(*) FROM users", factory); + + // Test function calls with multiple parameters + assertWhereStringTemplate("SELECT CONCAT(first_name, ' ', last_name) FROM users", + "SELECT CONCAT({@}.first_name, ' ', {@}.last_name) FROM users", factory); + + // Test nested function calls + assertWhereStringTemplate("SELECT UPPER(LOWER(name)) FROM users", + "SELECT UPPER(LOWER({@}.name)) FROM users", factory); + } + + @Test + public void testExtractAndTrimFunctions(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + + // Test EXTRACT function with FROM keyword + assertWhereStringTemplate("SELECT EXTRACT(YEAR FROM created_date) FROM users", + "SELECT EXTRACT(YEAR FROM {@}.created_date) FROM users", factory); + assertWhereStringTemplate("SELECT EXTRACT(MONTH FROM birth_date) FROM users", + "SELECT EXTRACT(MONTH FROM {@}.birth_date) FROM users", factory); + + // Test TRIM function with FROM keyword + assertWhereStringTemplate("SELECT TRIM(LEADING ' ' FROM name) FROM users", + "SELECT TRIM(LEADING ' ' FROM {@}.name) FROM users", factory); + assertWhereStringTemplate("SELECT TRIM(TRAILING 'x' FROM code) FROM products", + "SELECT TRIM(TRAILING 'x' FROM {@}.code) FROM products", factory); + } + + @Test + public void testQuotedIdentifiers(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + Dialect dialect = factory.getJdbcServices().getDialect(); + + // Test backtick-quoted identifiers + assertWhereStringTemplate("SELECT `user name` FROM users", + "SELECT {@}." + dialect.quote("`user name`") + " FROM users", factory); + assertWhereStringTemplate("SELECT `order-id` FROM orders", + "SELECT {@}." + dialect.quote("`order-id`") + " FROM orders", factory); + + // Test dialect-specific quoted identifiers + char openQuote = dialect.openQuote(); + char closeQuote = dialect.closeQuote(); + assertWhereStringTemplate("SELECT " + openQuote + "user name" + closeQuote + " FROM users", + "SELECT {@}." + dialect.quote("`user name`") + " FROM users", factory); + } + + @Test + public void testQualifiedIdentifiers(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + + // Test that already-qualified identifiers are not re-qualified + assertWhereStringTemplate("SELECT u.name, o.order_id FROM users u JOIN orders o ON u.id = o.user_id", + "SELECT u.name, o.order_id FROM users u JOIN orders o ON u.id = o.user_id", factory); + + // Test mixed qualified and unqualified identifiers + assertWhereStringTemplate("SELECT u.name, status FROM users u WHERE u.id = 1", + "SELECT u.name, {@}.status FROM users u WHERE u.id = 1", factory); + + // Test table-qualified identifiers + assertWhereStringTemplate("SELECT users.name, users.email FROM users", + "SELECT users.name, users.email FROM users", factory); + } + + @Test + public void testKeywordsAndIdentifiers(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + + // Test that SQL keywords are not qualified + assertWhereStringTemplate("SELECT * FROM users WHERE name IS NOT NULL", + "SELECT * FROM users WHERE {@}.name IS NOT NULL", factory); + assertWhereStringTemplate("SELECT DISTINCT name FROM users", + "SELECT DISTINCT {@}.name FROM users", factory); + assertWhereStringTemplate("SELECT name FROM users ORDER BY name", + "SELECT {@}.name FROM users ORDER BY {@}.name", factory); + + // Test soft keywords that can be column names + assertWhereStringTemplate("SELECT date, time FROM events", + "SELECT {@}.date, {@}.time FROM events", factory); + + // Test soft keywords in CURRENT expressions + assertWhereStringTemplate("SELECT * FROM events WHERE event_date > CURRENT_DATE", + "SELECT * FROM events WHERE {@}.event_date > CURRENT_DATE", factory); + } + + @Test + public void testComplexExpressions(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + + // Test complex expressions with multiple operators + assertWhereStringTemplate("SELECT name FROM users WHERE age >= 18 AND age <= 65", + "SELECT {@}.name FROM users WHERE {@}.age >= 18 AND {@}.age <= 65", factory); + assertWhereStringTemplate("SELECT price FROM products WHERE price BETWEEN 10 AND 100", + "SELECT {@}.price FROM products WHERE {@}.price BETWEEN 10 AND 100", factory); + + // Test expressions with parentheses + assertWhereStringTemplate("SELECT name FROM users WHERE (age > 18) AND (status = 'active')", + "SELECT {@}.name FROM users WHERE ({@}.age > 18) AND ({@}.status = 'active')", factory); + + // Test expressions with arithmetic operators + assertWhereStringTemplate("SELECT name FROM products WHERE price * 1.1 > 100", + "SELECT {@}.name FROM products WHERE {@}.price * 1.1 > 100", factory); + } + + @Test + public void testOrderedSetAggregationStateTransitions(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + + // Test that state transitions work correctly after ordered-set aggregation + assertWhereStringTemplate("SELECT LISTAGG(name, ', ') WITHIN GROUP (ORDER BY id) FROM users WHERE active = true", + "SELECT LISTAGG({@}.name, ', ') WITHIN GROUP (ORDER BY {@}.id) FROM users WHERE {@}.active = true", factory); + + // Test that identifiers after GROUP are qualified again + assertWhereStringTemplate("SELECT LISTAGG(name, ', ') WITHIN GROUP (ORDER BY id), status FROM users", + "SELECT LISTAGG({@}.name, ', ') WITHIN GROUP (ORDER BY {@}.id), {@}.status FROM users", factory); + + // Test non-LISTAGG ordered-set aggregates + assertWhereStringTemplate("SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary), department FROM employees", + "SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY {@}.salary), {@}.department FROM employees", factory); + } + + @Test + public void testLookaheadFunctionality(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + + // Test lookahead for function calls with whitespace + assertWhereStringTemplate("SELECT UPPER (name) FROM users", + "SELECT UPPER ({@}.name) FROM users", factory); + assertWhereStringTemplate("SELECT COUNT ( * ) FROM users", + "SELECT COUNT ( * ) FROM users", factory); + + // Test lookahead for CURRENT expressions with whitespace + assertWhereStringTemplate("SELECT * FROM events WHERE timestamp > CURRENT TIMESTAMP", + "SELECT * FROM events WHERE timestamp > CURRENT TIMESTAMP", factory); + assertWhereStringTemplate("SELECT * FROM events WHERE date > CURRENT DATE", + "SELECT * FROM events WHERE {@}.date > CURRENT DATE", factory); + } + private static void assertWhereStringTemplate(String sql, SessionFactoryImplementor sf) { assertEquals( sql, Template.renderWhereStringTemplate( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/timezones/UTCNormalizedInstantTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/timezones/UTCNormalizedInstantTest.java index 1b1141031781..78f1129bb945 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/timezones/UTCNormalizedInstantTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/timezones/UTCNormalizedInstantTest.java @@ -16,7 +16,7 @@ import org.hibernate.testing.orm.junit.SkipForDialect; import org.hibernate.type.descriptor.DateTimeUtils; -import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProvider; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; @@ -71,7 +71,7 @@ else if ( dialect.getDefaultTimestampPrecision() == 6 ) { @Test void testWithSystemTimeZone(SessionFactoryScope scope) { final TimeZone timeZoneBefore = TimeZone.getDefault(); TimeZone.setDefault( TimeZone.getTimeZone( "CET" ) ); - SharedDriverManagerConnectionProviderImpl.getInstance().onDefaultTimeZoneChange(); + SharedDriverManagerConnectionProvider.getInstance().onDefaultTimeZoneChange(); try { final Instant instant; final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect(); @@ -109,7 +109,7 @@ else if ( dialect.getDefaultTimestampPrecision() == 6 ) { } finally { TimeZone.setDefault( timeZoneBefore ); - SharedDriverManagerConnectionProviderImpl.getInstance().onDefaultTimeZoneChange(); + SharedDriverManagerConnectionProvider.getInstance().onDefaultTimeZoneChange(); } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/IndividuallySchemaValidatorImplConnectionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/IndividuallySchemaValidatorImplConnectionTest.java index dcfb39e19fe0..95f389cd3198 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/IndividuallySchemaValidatorImplConnectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/IndividuallySchemaValidatorImplConnectionTest.java @@ -24,7 +24,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.H2Dialect; import org.hibernate.engine.config.spi.ConfigurationService; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.PropertiesHelper; import org.hibernate.service.spi.ServiceRegistryImplementor; @@ -77,14 +77,14 @@ public class IndividuallySchemaValidatorImplConnectionTest extends BaseUnitTestC private ExecutionOptions executionOptions; - private DriverManagerConnectionProviderImpl connectionProvider; + private DriverManagerConnectionProvider connectionProvider; private Connection connection; @Before public void setUp() throws Exception { connectionProvider = - new DriverManagerConnectionProviderImpl(); + new DriverManagerConnectionProvider(); connectionProvider.configure( PropertiesHelper.map( properties() ) ); connection = connectionProvider.getConnection(); @@ -142,8 +142,8 @@ public void testMissingEntityContainsUnqualifiedEntityName() throws Exception { .applySettings( settings ) .build(); - DriverManagerConnectionProviderImpl connectionProvider = - new DriverManagerConnectionProviderImpl(); + DriverManagerConnectionProvider connectionProvider = + new DriverManagerConnectionProvider(); connectionProvider.configure( PropertiesHelper.map( properties() ) ); final GenerationTargetToDatabase schemaGenerator = new GenerationTargetToDatabase( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/IndividuallySchemaValidatorImplTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/IndividuallySchemaValidatorImplTest.java index 58f14a0f7d31..e1b09937cb7c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/IndividuallySchemaValidatorImplTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/IndividuallySchemaValidatorImplTest.java @@ -21,7 +21,7 @@ import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.dialect.H2Dialect; import org.hibernate.engine.config.spi.ConfigurationService; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.PropertiesHelper; import org.hibernate.service.spi.ServiceRegistryImplementor; @@ -157,8 +157,8 @@ public void testMissingColumn() { .applySettings( settings ) .build(); - DriverManagerConnectionProviderImpl connectionProvider = - new DriverManagerConnectionProviderImpl(); + DriverManagerConnectionProvider connectionProvider = + new DriverManagerConnectionProvider(); connectionProvider.configure( PropertiesHelper.map( properties() ) ); final GenerationTargetToDatabase schemaGenerator = new GenerationTargetToDatabase( @@ -214,8 +214,8 @@ public void testMismatchColumnType() { .applySettings( settings ) .build(); - DriverManagerConnectionProviderImpl connectionProvider = - new DriverManagerConnectionProviderImpl(); + DriverManagerConnectionProvider connectionProvider = + new DriverManagerConnectionProvider(); connectionProvider.configure( PropertiesHelper.map( properties() ) ); final GenerationTargetToDatabase schemaGenerator = new GenerationTargetToDatabase( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/MySQLColumnValidationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/MySQLColumnValidationTest.java index be6f9b4925b3..c72b38a1a1e9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/MySQLColumnValidationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/MySQLColumnValidationTest.java @@ -15,7 +15,7 @@ import org.hibernate.cfg.Environment; import org.hibernate.dialect.MySQLDialect; import org.hibernate.engine.config.spi.ConfigurationService; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.internal.util.PropertiesHelper; import org.hibernate.tool.schema.internal.DefaultSchemaFilter; import org.hibernate.tool.schema.internal.ExceptionHandlerLoggedImpl; @@ -66,11 +66,11 @@ @RequiresDialect( value = MySQLDialect.class, matchSubTypes = false ) public class MySQLColumnValidationTest { - private DriverManagerConnectionProviderImpl connectionProvider; + private DriverManagerConnectionProvider connectionProvider; @BeforeAll public void init() { - connectionProvider = new DriverManagerConnectionProviderImpl(); + connectionProvider = new DriverManagerConnectionProvider(); connectionProvider.configure( PropertiesHelper.map( Environment.getProperties() ) ); try( Connection connection = connectionProvider.getConnection(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/type/AbstractJavaTimeTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/AbstractJavaTimeTypeTest.java index 4c5eeb3c4779..690c1aca29b3 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/type/AbstractJavaTimeTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/AbstractJavaTimeTypeTest.java @@ -28,7 +28,7 @@ import org.hibernate.service.ServiceRegistry; import org.hibernate.testing.orm.junit.JiraKey; -import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProvider; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.CustomParameterized; import org.hibernate.testing.orm.junit.DialectContext; @@ -194,7 +194,7 @@ public void nativeWriteThenRead() { protected final void withDefaultTimeZone(Runnable runnable) { TimeZone timeZoneBefore = TimeZone.getDefault(); TimeZone.setDefault( toTimeZone( env.defaultJvmTimeZone ) ); - SharedDriverManagerConnectionProviderImpl.getInstance().onDefaultTimeZoneChange(); + SharedDriverManagerConnectionProvider.getInstance().onDefaultTimeZoneChange(); /* * Run the code in a new thread, because some libraries (looking at you, h2 JDBC driver) * cache data dependent on the default timezone in thread local variables, @@ -223,7 +223,7 @@ else if ( cause instanceof Error ) { } finally { TimeZone.setDefault( timeZoneBefore ); - SharedDriverManagerConnectionProviderImpl.getInstance().onDefaultTimeZoneChange(); + SharedDriverManagerConnectionProvider.getInstance().onDefaultTimeZoneChange(); } } diff --git a/hibernate-core/src/test/java/x/IdClassSingleOneToOneTest.java b/hibernate-core/src/test/java/x/IdClassSingleOneToOneTest.java new file mode 100644 index 000000000000..be3f172e2d31 --- /dev/null +++ b/hibernate-core/src/test/java/x/IdClassSingleOneToOneTest.java @@ -0,0 +1,104 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package x; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.OneToOne; +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.Test; + +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + + +@DomainModel( annotatedClasses = { + IdClassSingleOneToOneTest.EntityA.class, + IdClassSingleOneToOneTest.EntityB.class, +} ) +@SessionFactory +public class IdClassSingleOneToOneTest { + + @Test + public void test(SessionFactoryScope scope) { + scope.getSessionFactory(); + scope.inTransaction( session -> { + EntityA entityA = new EntityA(3); + EntityB entityB = new EntityB( entityA ); + entityA.entityB = entityB; + session.persist( entityA ); + session.persist( entityB ); + assertEquals( new EntityBId(3), + session.getIdentifier( entityB ) ); + } ); + scope.inTransaction( session -> { + EntityB entityB = session.find( EntityB.class, new EntityBId(3) ); + assertNotNull( entityB ); + } ); + } + + @Entity( name = "EntityA" ) + static class EntityA { + + @Id + private Integer id; + + @OneToOne( mappedBy = "entityA", fetch = FetchType.LAZY ) + private EntityB entityB; + + public EntityA() { + } + + public EntityA(Integer id) { + this.id = id; + } + + } + + @IdClass( EntityBId.class ) + @Entity( name = "EntityB" ) + static class EntityB { + + @Id + @OneToOne( fetch = FetchType.LAZY ) + private EntityA entityA; + + public EntityB() { + } + + public EntityB(EntityA entityA) { + this.entityA = entityA; + } + + } + + static class EntityBId { + private final Integer entityA; + + EntityBId() { + entityA = null; + } + EntityBId(Integer entityA) { + this.entityA = entityA; + } + + @Override + public boolean equals(Object o) { + return o instanceof EntityBId entityBId + && Objects.equals( entityA, entityBId.entityA ); + } + + @Override + public int hashCode() { + return Objects.hashCode( entityA ); + } + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/env/ConnectionProviderBuilder.java b/hibernate-testing/src/main/java/org/hibernate/testing/env/ConnectionProviderBuilder.java index e0cf1cb42cf2..fd837a44616f 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/env/ConnectionProviderBuilder.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/env/ConnectionProviderBuilder.java @@ -17,8 +17,8 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; -import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DataSourceConnectionProvider; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.internal.util.PropertiesHelper; import org.hibernate.internal.util.ReflectHelper; @@ -59,7 +59,7 @@ public static Properties getConnectionProviderProperties(String dbName, Map PGJDBC_STANDARD_TYPE_NAMES = buildTypeNames( Set.of( "int2", @@ -70,7 +70,7 @@ private static Set buildTypeNames(Set baseTypeNames) { return typeNames; } - public static SharedDriverManagerConnectionProviderImpl getInstance() { + public static SharedDriverManagerConnectionProvider getInstance() { return INSTANCE; } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SharedDriverManagerTypeCacheClearingIntegrator.java b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SharedDriverManagerTypeCacheClearingIntegrator.java index b8ea1031d74c..6118d2db2796 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SharedDriverManagerTypeCacheClearingIntegrator.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SharedDriverManagerTypeCacheClearingIntegrator.java @@ -15,6 +15,6 @@ public void integrate( Metadata metadata, BootstrapContext bootstrapContext, SessionFactoryImplementor sessionFactory) { - SharedDriverManagerConnectionProviderImpl.getInstance().clearTypeCache(); + SharedDriverManagerConnectionProvider.getInstance().clearTypeCache(); } } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/jta/JtaAwareConnectionProviderImpl.java b/hibernate-testing/src/main/java/org/hibernate/testing/jta/JtaAwareConnectionProviderImpl.java index b2b67aeed2bb..c1cfd49be905 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/jta/JtaAwareConnectionProviderImpl.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/jta/JtaAwareConnectionProviderImpl.java @@ -53,7 +53,7 @@ import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator; -import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.DatabaseConnectionInfo; import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData; @@ -62,7 +62,7 @@ import org.hibernate.service.spi.ServiceRegistryAwareService; import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.service.spi.Stoppable; -import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProvider; /** * A {@link ConnectionProvider} implementation intended for testing Hibernate/JTA interaction. In that limited scope we @@ -76,7 +76,7 @@ public class JtaAwareConnectionProviderImpl implements ConnectionProvider, Confi ServiceRegistryAwareService { private static final String CONNECTION_KEY = "_database_connection"; - private final DriverManagerConnectionProviderImpl delegate = SharedDriverManagerConnectionProviderImpl.getInstance(); + private final DriverManagerConnectionProvider delegate = SharedDriverManagerConnectionProvider.getInstance(); private final List nonEnlistedConnections = new ArrayList<>(); @@ -102,7 +102,7 @@ public void configure(Map configurationValues) { connectionSettings.put( Environment.AUTOCOMMIT, "false" ); connectionSettings.put( Environment.POOL_SIZE, "5" ); - connectionSettings.put( DriverManagerConnectionProviderImpl.INITIAL_SIZE, "0" ); + connectionSettings.put( DriverManagerConnectionProvider.INITIAL_SIZE, "0" ); delegate.configure( connectionSettings ); } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/jdbc/PreparedStatementSpyConnectionProvider.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/jdbc/PreparedStatementSpyConnectionProvider.java index f8a28db2e67f..ff0d73829516 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/jdbc/PreparedStatementSpyConnectionProvider.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/jdbc/PreparedStatementSpyConnectionProvider.java @@ -18,7 +18,7 @@ import org.hibernate.testing.jdbc.ConnectionProviderDelegate; import org.hibernate.testing.jdbc.JdbcSpies; -import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProvider; /** * This {@link ConnectionProvider} extends any other ConnectionProvider that would be used by default taken the current configuration properties, and it @@ -61,7 +61,7 @@ public PreparedStatementSpyConnectionProvider() { public PreparedStatementSpyConnectionProvider(boolean forceSupportsAggressiveRelease) { super(forceSupportsAggressiveRelease); - setConnectionProvider( SharedDriverManagerConnectionProviderImpl.getInstance() ); + setConnectionProvider( SharedDriverManagerConnectionProvider.getInstance() ); } protected Connection actualConnection() throws SQLException { diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/jdbc/TimeZoneConnectionProvider.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/jdbc/TimeZoneConnectionProvider.java index 1aafc205fdcd..29aae4c3c5e7 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/jdbc/TimeZoneConnectionProvider.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/jdbc/TimeZoneConnectionProvider.java @@ -9,7 +9,7 @@ import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.jdbc.ConnectionProviderDelegate; -import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProvider; /** * This {@link ConnectionProvider} extends any other ConnectionProvider that would be used by default taken the current configuration properties, and it @@ -28,7 +28,7 @@ public TimeZoneConnectionProvider(String customTimeZone) { this.defaultTimeZone = System.setProperty( "user.timezone", customTimeZone); TimeZone.setDefault(TimeZone.getTimeZone( customTimeZone )); // Clear the connection pool to avoid issues with drivers that initialize the session TZ to the system TZ - SharedDriverManagerConnectionProviderImpl.getInstance().onDefaultTimeZoneChange(); + SharedDriverManagerConnectionProvider.getInstance().onDefaultTimeZoneChange(); } @Override @@ -37,6 +37,6 @@ public void stop() { System.setProperty( "user.timezone", defaultTimeZone); TimeZone.setDefault(TimeZone.getTimeZone( defaultTimeZone )); // Clear the connection pool to avoid issues with drivers that initialize the session TZ to the system TZ - SharedDriverManagerConnectionProviderImpl.getInstance().onDefaultTimeZoneChange(); + SharedDriverManagerConnectionProvider.getInstance().onDefaultTimeZoneChange(); } } 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 7bb583f3801f..9476ef9707b3 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 @@ -453,7 +453,6 @@ public boolean apply(Dialect dialect) { public static class SupportsCteInsertStrategy implements DialectFeatureCheck { public boolean apply(Dialect dialect) { return dialect instanceof PostgreSQLDialect - || dialect instanceof CockroachDialect || dialect instanceof DB2Dialect || dialect instanceof GaussDBDialect; } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/util/ServiceRegistryUtil.java b/hibernate-testing/src/main/java/org/hibernate/testing/util/ServiceRegistryUtil.java index 36f8135cd336..417f0da4e9a8 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/util/ServiceRegistryUtil.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/util/ServiceRegistryUtil.java @@ -13,7 +13,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; -import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProvider; public class ServiceRegistryUtil { @@ -46,7 +46,7 @@ public static StandardServiceRegistryBuilder applySettings(StandardServiceRegist && !builder.getSettings().containsKey( AvailableSettings.CONNECTION_PROVIDER ) ) { builder.applySetting( AvailableSettings.CONNECTION_PROVIDER, - SharedDriverManagerConnectionProviderImpl.getInstance() + SharedDriverManagerConnectionProvider.getInstance() ); builder.applySetting( AvailableSettings.CONNECTION_PROVIDER_DISABLES_AUTOCOMMIT, @@ -62,7 +62,7 @@ public static void applySettings(Map properties) { final Map objectMap = (Map) properties; objectMap.put( AvailableSettings.CONNECTION_PROVIDER, - SharedDriverManagerConnectionProviderImpl.getInstance() + SharedDriverManagerConnectionProvider.getInstance() ); objectMap.put( AvailableSettings.CONNECTION_PROVIDER_DISABLES_AUTOCOMMIT, diff --git a/migration-guide.adoc b/migration-guide.adoc index c46a8ee7c439..255889667234 100644 --- a/migration-guide.adoc +++ b/migration-guide.adoc @@ -1,7 +1,7 @@ = 7.1 Migration Guide :toc: :toclevels: 4 -:version: 7.1 +:version: 7.2 :docsBase: https://docs.jboss.org/hibernate/orm :versionDocBase: {docsBase}/{version} :userGuideBase: {versionDocBase}/userguide/html_single/Hibernate_User_Guide.html @@ -19,7 +19,7 @@ earlier versions, see any other pertinent migration guides as well. [[requirements]] == Requirements -See the link:{releaseSeriesBase}[website] for the list of requirements for the 7.1 series. +See the link:{releaseSeriesBase}[website] for the list of requirements for the {version} series. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // New Features @@ -28,7 +28,7 @@ See the link:{releaseSeriesBase}[website] for the list of requirements for the 7 [[new-features]] == New Features -See the link:{releaseSeriesBase}#whats-new[website] for the list of new features in the 7.1 series. +See the link:{releaseSeriesBase}#whats-new[website] for the list of new features in the {version} series. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -40,84 +40,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]. -[[lock-options]] -=== LockOptions - -7.0 begins the process of reducing the visibility of `org.hibernate.LockOptions`. -This class was originally exposed as an API, even though it is more properly an SPI. -From an API perspective, `FindOption`, `LockOption` and `RefreshOption` are much better ways to specify this information. -The class itself, as well as API methods which expose it, have been deprecated. - -However, 2 aspects of `LockOptions` have been completely removed. -First, `LockOptions` previously defined a number of constants which have been removed. -Secondly, the ability to define different LockModes for individual aliases has been removed. - -[NOTE] ----- -ALias-specific LockModes was a very misleading feature, as Hibernate would only ever use the "greatest" LockMode. -We plan to add the ability to specify a list of aliases to be locked in a later release. -See https://hibernate.atlassian.net/browse/HHH-19664 for details. ----- - -Application code using `org.hibernate.LockOptions` should migrate to using `FindOption`, -`LockOption` and `RefreshOption` to control the various aspects of locking. E.g. - -[source,java] ----- -LockOptions lockOptions = new LockOptions(); -lockOptions.setLockMode(PESSIMISTIC_WRITE); -lockOptions.setTimeout(1000); -session.refresh(book, lockOptions); ----- - -can instead be written as - -[source,java] ----- -session.refresh(book, - PESSIMISTIC_WRITE, - Timeout.milliseconds(1000)); ----- - -[[enhancement-options]] -=== Bytecode Enhancement Options - -We plan to remove two options of bytecode enhancement and have currently marked them as deprecated. - -First is bidirectional association management. -Applications should instead manage both sides of such associations directly as we have always recommended. - -Second is a little-known feature called "extended" enhancement. -Applications should instead use proper object-oriented encapsulation, exposing managed state via getters and setters. - -Additionally, attempting to re-enhance a class with different options is no longer allowed and will result in a `FeatureMismatchException`. - -The Gradle plugin configuration has changed slightly. When using the default setup: - -``` -hibernate { enhancement } -``` - -it should be updated to: - -``` -hibernate { - enhancement {} -} -``` - -in order to properly enable Bytecode Enhancement. - -[[session-getLobHelper]] -=== Session#getLobHelper - -The `Session#getLobHelper` method has been marked as deprecated in favor of the static `Hibernate#getLobHelper` and will be removed in a future *major* version. - -[[H2-lock-timeout]] -=== WAIT, NO WAIT and SKIP LOCKED for H2 - -The for-update clause enhancements of H2 2.2.220 are now reflected in the Hibernate ORM dialect, allowing applications to use `wait`, `no wait` and `skip locked` options. - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // SPI changes @@ -126,49 +48,7 @@ The for-update clause enhancements of H2 2.2.220 are now reflected in the Hibern [[spi-changes]] == Changes to SPI -[[force-increment]] -=== Force-increment Locking for Version with Custom Generator - -A new event type (`FORCE_INCREMENT`) has been added to -`org.hibernate.generator.EventType` to support -`OPTIMISTIC_FORCE_INCREMENT` and `PESSIMISTIC_FORCE_INCREMENT` -when used with `@Version` mappings with custom generators. - - -[[pessimistic-locking]] -=== Pessimistic Locking - -A number of changes have been made to pessimistic locking support. - -* Introduction of `org.hibernate.dialect.lock.spi.LockingSupport` which represents a Dialect's support for pessimistic locking. -* Introduction of `org.hibernate.sql.ast.spi.LockingClauseStrategy` to integrate into SQL AST translation. -* Changed standard implementation of `org.hibernate.dialect.lock.LockingStrategy` for pessimistic locking to one using SQL AST. -* Changes to `LockOptions` as <>. - -[NOTE] -More changes related to pessimistic locking are coming in later 7.x releases. -We've opted to tackle these in stages since they all relate and build on top of each other. -See https://hibernate.atlassian.net/browse/HHH-19551 for details. - -[[format-mapper]] -=== JSON FormatMapper Enhancements - -JSON mappings on Oracle 21+ will now try to leverage driver built-in OSON decoding to improve JSON parsing performance. In case of trouble, this can be disabled with the `hibernate.dialect.oracle.oson_format_disabled` configuration option. - - -[[enhancement-option-granularity]] -=== Enhancement Option Granularity - -The actual enhancements written into bytecode were previously allowed to vary by class and sometimes even by attribute. -However, all Hibernate tooling only supported setting those "globally" per enhancement. -To this end, all `org.hibernate.bytecode.enhance.spi.EnhancementContext` methods which determine whether certain aspects of enhancement are applied have changed to no longer accept class/attribute. -Specifically: - -* `doDirtyCheckingInline(UnloadedClass classDescriptor)` -> `doDirtyCheckingInline()` -* `doExtendedEnhancement(UnloadedClass classDescriptor)` -> `doExtendedEnhancement()` -* `doBiDirectionalAssociationManagement(UnloadedField field)` -> `doBiDirectionalAssociationManagement()` - -See also <>. +This section describes changes to contracts (classes, interfaces, methods, etc.) which are considered https://hibernate.org/community/compatibility-policy/#spi[SPI]. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -181,19 +61,9 @@ See also <>. This section describes changes to DDL generated by the schema export tooling. Such changes typically do not impact programs using a relational schema managed externally to Hibernate. -[[single-table-check]] -=== Automatic check constraints with single table inheritance mappings - -Previously, the non-nullability of the column mapped by an attribute declared `optional=false` by a subclass in a single table inheritance hierarchy was not enforced by the database. -Hibernate now automatically generates DDL `check` constraints to enforce the non-nullability of such columns. - [[dependency-changes]] == Changes in Dependencies This section describes changes to dependencies used by Hibernate ORM. -[[dependency-agroal]] -=== Inclusion of Agroal Pool - -`agroal-pool` is now a transitive implementation (runtime) dependency of the `hibernate-agroal` module. Applications using `hibernate-agroal` no longer need to manually depend on it. diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/generic/DataRepositoryGenericParametersTest.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/generic/DataRepositoryGenericParametersTest.java new file mode 100644 index 000000000000..8f77a716ec5c --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/generic/DataRepositoryGenericParametersTest.java @@ -0,0 +1,466 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.processor.test.data.generic; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.data.Order; +import jakarta.data.repository.CrudRepository; +import jakarta.data.repository.Find; +import jakarta.data.repository.Repository; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import org.hibernate.processor.test.util.CompilationTest; +import org.hibernate.processor.test.util.IgnoreCompilationErrors; +import org.hibernate.processor.test.util.WithClasses; +import org.junit.jupiter.api.Test; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.StringJoiner; +import java.util.UUID; + +import static java.util.Arrays.stream; +import static java.util.Collections.addAll; +import static java.util.Comparator.comparing; +import static java.util.Objects.requireNonNullElse; +import static java.util.Objects.requireNonNullElseGet; +import static org.hibernate.processor.test.util.TestUtil.assertMetamodelClassGeneratedFor; +import static org.hibernate.processor.test.util.TestUtil.getMetaModelSourceAsString; +import static org.hibernate.processor.test.util.TestUtil.getMetamodelClassFor; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +@CompilationTest +public class DataRepositoryGenericParametersTest { + + @Test + @IgnoreCompilationErrors + @WithClasses({Cat.class, CatRepository.class}) + public void test() throws NoSuchMethodException { + final var repositoryClass = CatRepository.class; + + System.out.println( getMetaModelSourceAsString( repositoryClass ) ); + assertMetamodelClassGeneratedFor( repositoryClass ); + + final var types = new HashMap, Map>(); + collectTypeParameters( repositoryClass, types, Object.class ); + + final var actualTypes = ActualTypes.actualTypes( repositoryClass ); + + final Class metamodelClass = getMetamodelClassFor( repositoryClass ); + + final var methodList = new HashSet(); + getMethods( repositoryClass, methodList ); + + final var methods = methodList.stream().sorted( comparing( Method::getName ) ).toList(); + for ( final var method : methods ) { + final Class methodDeclaringClass = method.getDeclaringClass(); + + final var genericParameterTypes = method.getGenericParameterTypes(); + final var actualTypeArgumentsX = stream( genericParameterTypes ) + .map( t -> actualTypes.getActualType( t, methodDeclaringClass ) ) + .toArray( Type[]::new ); + final var classes = stream( actualTypeArgumentsX ).map( DataRepositoryGenericParametersTest::toClass ).toArray( Class[]::new ); + + final var method1 = metamodelClass.getMethod( method.getName(), classes ); + + final var genericParameterTypes1 = method1.getGenericParameterTypes(); + + for ( var n = 0; n < genericParameterTypes.length; ++n ) { + final var expected = actualTypeArgumentsX[n]; + final var actual = genericParameterTypes1[n]; + assertFalse( + expected instanceof ParameterizedType && actual instanceof Class, + "Failed for method " + method.toGenericString() + ); + final var expectedTypeName = expected.getTypeName(); + final var actualTypeName = actual.getTypeName(); + assertEquals( expectedTypeName, actualTypeName, + "Failed for parameter %d of method %s".formatted( n, method.toGenericString() ) ); + } + } + } + + private static Class toClass(Type t) { + if ( Objects.requireNonNull( t ) instanceof Class c ) { + return c; + } + else if ( t instanceof ParameterizedType pt ) { + return (Class) pt.getRawType(); + } + else if ( t instanceof TypeVariable tv ) { + return (Class) tv.getBounds()[0]; + } + else if ( t instanceof GenericArrayType at ) { + final var componentType = at.getGenericComponentType(); + final var ct = toClass( componentType ); + return ct.arrayType(); + } + throw new IllegalStateException( "Unexpected value: " + t ); + } + + private void collectTypeParameters(Type type, Map, Map> acc, Type subType) + throws IllegalStateException { + Class key; + if ( Objects.requireNonNull( subType ) instanceof ParameterizedType pt ) { + key = (Class) pt.getRawType(); + } + else if ( subType instanceof Class cls ) { + key = cls; + } + else { + throw new IllegalStateException( "Unexpected value: " + subType ); + } + final var paramsMap = requireNonNullElseGet( + acc.get( key ), + Map::of ); + final Class clazz; + if ( type instanceof ParameterizedType pt ) { + final var params = new HashMap(); + //noinspection rawtypes + clazz = (Class) pt.getRawType(); + final var typeParameters = clazz.getTypeParameters(); + final var actualTypeArguments = pt.getActualTypeArguments(); + for ( var n = 0; n < typeParameters.length; ++n ) { + final var typeName = actualTypeArguments[n].getTypeName(); + params.put( typeParameters[n].getName(), + requireNonNullElse( paramsMap.get( typeName ), typeName ) ); + } + acc.put( clazz, params ); + } + else if ( type instanceof Class cls ) { + acc.put( cls, Map.of() ); + clazz = cls; + } + else { + throw new IllegalStateException( "Unexpected value: %s".formatted( type ) ); + } + for ( final var itype : clazz.getGenericInterfaces() ) { + collectTypeParameters( itype, acc, type ); + } + } + + private void getMethods(Class clazz, Collection methods) { + addAll( methods, clazz.getDeclaredMethods() ); + for ( final var iface : clazz.getInterfaces() ) { + getMethods( iface, methods ); + } + } + + private record AnnotatedTypeImpl(AnnotatedType annotatedType, Type actualType) implements AnnotatedType { + + @Override + public Type getType() { + return actualType; + } + + @Override + public T getAnnotation(@Nonnull Class annotationClass) { + return annotatedType.getAnnotation( annotationClass ); + } + + @Override + @Nonnull + public Annotation[] getAnnotations() { + return annotatedType.getAnnotations(); + } + + @Override + @Nonnull + public Annotation[] getDeclaredAnnotations() { + return annotatedType.getDeclaredAnnotations(); + } + } + + private record TypeVariableImpl(TypeVariable tv, Type[] bounds, AnnotatedType[] annotatedBounds) + implements TypeVariable { + + @Override + public String getTypeName() { + return tv.getTypeName(); + } + + @Override + @Nullable + public T getAnnotation(@Nonnull Class annotationClass) { + return tv.getAnnotation( annotationClass ); + } + + @Override + @Nonnull + public Annotation[] getAnnotations() { + return tv.getAnnotations(); + } + + @Override + @Nonnull + public Annotation[] getDeclaredAnnotations() { + return tv.getDeclaredAnnotations(); + } + + @Override + @Nonnull + public Type[] getBounds() { + return bounds; + } + + @Override + public GenericDeclaration getGenericDeclaration() { + return tv.getGenericDeclaration(); + } + + @Override + public String getName() { + return tv.getName(); + } + + @Override + public @Nonnull AnnotatedType[] getAnnotatedBounds() { + return annotatedBounds; + } + } + + private record WildcardTypeImpl(Type[] upper, Type[] lower) implements WildcardType { + @Override + public String getTypeName() { + return "?" + (upper.length > 0 ? " extends " + upper[0].getTypeName() : "") + + (lower.length > 0 ? " super " + lower[0].getTypeName() : ""); + } + + @Override + @Nonnull + public Type[] getUpperBounds() { + return upper; + } + + @Override + @Nonnull + public Type[] getLowerBounds() { + return lower; + } + } + + private record ParameterizedTypeImpl(Type rawType, Type[] typeArguments, @Nullable Type ownerType) + implements ParameterizedType { + @Override + public Type[] getActualTypeArguments() { + return typeArguments; + } + + @Override + public Type getRawType() { + return rawType; + } + + @Override + @Nullable + public Type getOwnerType() { + return ownerType; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + if ( ownerType != null ) { + sb.append( ownerType.getTypeName() ); + + sb.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() + "$", + "" + ) + ); + } + else if ( rawType instanceof Class clazz ) { + sb.append( clazz.getSimpleName() ); + } + else { + sb.append( rawType.getTypeName() ); + } + } + else { + sb.append( rawType.getTypeName() ); + } + + if ( typeArguments != null ) { + final StringJoiner sj = new StringJoiner( ", ", "<", ">" ); + sj.setEmptyValue( "" ); + for ( Type t : typeArguments ) { + sj.add( t.getTypeName() ); + } + sb.append( sj ); + } + + return sb.toString(); + } + } + + private static class ActualTypes { + private final Map, Map> types = new HashMap<>(); + + public static ActualTypes actualTypes(Class declaring) { + return new ActualTypes().collectGenericsParameters( declaring, Object.class ); + } + + private ActualTypes collectGenericsParameters(Type type, Type subType) + throws IllegalStateException { + Class key; + if ( Objects.requireNonNull( subType ) instanceof ParameterizedType pt ) { + key = (Class) pt.getRawType(); + } + else if ( subType instanceof Class cls ) { + key = cls; + } + else { + throw new IllegalStateException( "Unexpected value: " + subType ); + } + final var paramsMap = requireNonNullElseGet( + types.get( key ), + Map::of ); + final Class clazz; + if ( type instanceof ParameterizedType pt ) { + final var params = new HashMap(); + //noinspection rawtypes + clazz = (Class) pt.getRawType(); + final var typeParameters = clazz.getTypeParameters(); + final var actualTypeArguments = pt.getActualTypeArguments(); + for ( var n = 0; n < typeParameters.length; ++n ) { + final var argn = actualTypeArguments[n]; + params.put( typeParameters[n].getName(), + requireNonNullElse( paramsMap.get( argn.getTypeName() ), argn ) ); + } + types.put( clazz, params ); + } + else if ( type instanceof Class cls ) { + types.put( cls, Map.of() ); + clazz = cls; + } + else { + throw new IllegalStateException( "Unexpected value: %s".formatted( type ) ); + } + for ( final var itype : clazz.getGenericInterfaces() ) { + collectGenericsParameters( itype, type ); + } + return this; + } + + Type getActualType(@Nonnull Type type, Class declaring) { + if ( type instanceof TypeVariable tv ) { + final var type1 = requireNonNullElseGet( types.get( declaring ), Map::of ) + .get( tv.getName() ); + if ( type1 != null ) { + return type1; + } + final var bo = tv.getBounds(); + final var bounds = new Type[bo.length]; + for ( var n = 0; n < bo.length; ++n ) { + bounds[n] = getActualType( bo[n], declaring ); + } + final var ab = tv.getAnnotatedBounds(); + final var annotatedBounds = new AnnotatedType[ab.length]; + for ( var n = 0; n < ab.length; ++n ) { + final var annotatedType = ab[n]; + final var actualType = getActualType( annotatedType.getType(), declaring ); + final var type2 = + annotatedType.getType().equals( actualType ) ? annotatedType : + new AnnotatedTypeImpl( annotatedType, actualType ); + annotatedBounds[n] = type2; + } + return Arrays.equals( bo, bounds ) && Arrays.equals( ab, annotatedBounds ) ? + tv : new TypeVariableImpl( tv, bounds, annotatedBounds ); + } + else if ( type instanceof ParameterizedType pt ) { + final var typeArguments = pt.getActualTypeArguments(); + /*for ( var n = 0; n < typeArguments.length; ++n ) { + var typeArgument = typeArguments[n]; + if ( typeArgument instanceof WildcardType wt ) { + }*/ + final Type[] typeArgumentsX = new Type[typeArguments.length]; + for ( var n = 0; n < typeArguments.length; ++n ) { + typeArgumentsX[n] = getActualType( typeArguments[n], declaring ); + } + return Arrays.equals( typeArguments, typeArgumentsX ) ? pt : + new ParameterizedTypeImpl( pt.getRawType(), typeArgumentsX, pt.getOwnerType() ); + } + else if ( type instanceof Class cls ) { + return cls; + } + else if ( type instanceof WildcardType wt ) { + final var up = wt.getUpperBounds(); + final var upper = new Type[up.length]; + for ( var k = 0; k < up.length; ++k ) { + upper[k] = getActualType( up[k], declaring ); + } + final var lo = wt.getLowerBounds(); + final var lower = new Type[lo.length]; + for ( var k = 0; k < lo.length; ++k ) { + lower[k] = getActualType( lo[k], declaring ); + } + return Arrays.equals( up, upper ) && Arrays.equals( lo, lower ) ? wt : + new WildcardTypeImpl( upper, lower ); + } + else if ( type instanceof GenericArrayType at ) { + final var componentType = getActualType( at.getGenericComponentType(), declaring ); + return new GenericArrayType() { + + @Override + @Nonnull + public Type getGenericComponentType() { + return componentType; + } + + @Override + public String toString() { + return getGenericComponentType().getTypeName() + "[]"; + } + }; + } + throw new IllegalStateException( + "Unexpected value: %s".formatted( type ) ); + } + } + @Entity + public static class Cat { + + @Id + @GeneratedValue + UUID id; + + String name; + + Integer age; + + } + + @Repository + public interface CatRepository extends CrudRepository { + + @Find + List byPubDate(Order order); + + } + +} diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractAnnotatedMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractAnnotatedMethod.java index c661c03961fc..b96773b41baa 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractAnnotatedMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractAnnotatedMethod.java @@ -24,7 +24,7 @@ public abstract class AbstractAnnotatedMethod implements MetaAttribute { final AnnotationMetaEntity annotationMetaEntity; - private final ExecutableElement method; + final ExecutableElement method; final String sessionType; final String sessionName; 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 9bf0ce3c3b63..123589a1f799 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 @@ -1830,7 +1830,8 @@ else if ( returnArgument context.addNonnullAnnotation(), lifecycleParameterKind(parameterType), returnArgument, - hasGeneratedId(declaredType) + hasGeneratedId(declaredType), + element ) ); } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/LifecycleMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/LifecycleMethod.java index b054c386da15..4736c6545406 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/LifecycleMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/LifecycleMethod.java @@ -4,12 +4,24 @@ */ package org.hibernate.processor.annotation; - import javax.lang.model.element.ExecutableElement; - +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.IntersectionType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.WildcardType; +import java.util.Collection; import java.util.Set; import static java.lang.Character.toUpperCase; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toSet; +import static javax.lang.model.type.TypeKind.VOID; import static org.hibernate.processor.util.Constants.EVENT; import static org.hibernate.processor.util.Constants.INJECT; import static org.hibernate.processor.util.Constants.JD_LIFECYCLE_EVENT; @@ -17,6 +29,7 @@ import static org.hibernate.processor.util.Constants.NONNULL; import static org.hibernate.processor.util.Constants.TYPE_LITERAL; import static org.hibernate.processor.util.Constants.UNI; +import static org.hibernate.processor.util.TypeUtils.resolveTypeName; public class LifecycleMethod extends AbstractAnnotatedMethod { private final String entity; @@ -28,6 +41,8 @@ public class LifecycleMethod extends AbstractAnnotatedMethod { private final ParameterKind parameterKind; private final boolean returnArgument; private final boolean hasGeneratedId; + private final Collection methodTypeParameters; + private final TypeElement element; public enum ParameterKind { NORMAL, @@ -48,7 +63,8 @@ public LifecycleMethod( boolean addNonnullAnnotation, ParameterKind parameterKind, boolean returnArgument, - boolean hasGeneratedId) { + boolean hasGeneratedId, + TypeElement element) { super(annotationMetaEntity, method, sessionName, sessionType); this.entity = entity; this.actualEntity = actualEntity; @@ -59,6 +75,11 @@ public LifecycleMethod( this.parameterKind = parameterKind; this.returnArgument = returnArgument; this.hasGeneratedId = hasGeneratedId; + this.methodTypeParameters = method.getTypeParameters().stream() + .map( TypeParameterElement::asType ) + .map( TypeMirror::toString ) + .collect( toSet() ); + this.element = element; } @Override @@ -334,27 +355,86 @@ private boolean isGeneratedIdUpsert() { private void preamble(StringBuilder declaration) { declaration .append("\n@Override\npublic ") + .append(parameterTypeBopunds()) .append(returnType()) .append(' ') .append(methodName) .append('('); notNull(declaration); + final var parameters = method.getParameters(); + assert parameters.size() == 1; + final VariableElement element = parameters.get(0); declaration - .append(annotationMetaEntity.importType(entity)) + .append(resolveAsString(element.asType())) .append(' ') - .append(parameterName) + .append(element.getSimpleName()) .append(')') .append(" {\n"); } + private String parameterTypeBopunds() { + if ( method.getTypeParameters().isEmpty() ) { + return ""; + } + return method.getTypeParameters().stream() + .map( this::resolveTypeParameter ) + .collect( joining( ", ", " <", "> " ) ); + } + + private String resolveAsString(TypeMirror type) { + if ( type.getKind().isPrimitive() || type.getKind() == VOID ) { + return type.toString(); + } + else if ( type instanceof DeclaredType declaredType ) { + final var element = annotationMetaEntity.importType( + ((TypeElement) declaredType.asElement()).getQualifiedName().toString() + ); + if ( declaredType.getTypeArguments().isEmpty() ) { + return element; + } + return element + declaredType.getTypeArguments().stream().map( this::resolveAsString ) + .collect( joining( ",", "<", ">" ) ); + } + else if ( type instanceof TypeVariable typeVariable ) { + final var value = typeVariable.toString(); + if ( methodTypeParameters.contains( value ) ) { + return value; + } + return annotationMetaEntity.importType( resolveTypeName( element, method.getEnclosingElement(), value ) ); + } + else if ( type instanceof WildcardType wildcardType ) { + return "?" + + (wildcardType.getExtendsBound() == null ? "" + : " extends " + resolveAsString( wildcardType.getExtendsBound() )) + + (wildcardType.getSuperBound() == null ? "" + : " super " + resolveAsString( wildcardType.getExtendsBound() )); + } + else if ( type instanceof ArrayType arrayType ) { + return resolveAsString( arrayType.getComponentType() ) + "[]"; + } + else if ( type instanceof IntersectionType intersectionType ) { + return intersectionType.getBounds().stream().map( this::resolveAsString ).collect( joining( "&" ) ); + } + else { + return type.toString(); + } + } + + private String resolveTypeParameter(TypeParameterElement p) { + final var type = (TypeVariable) p.asType(); + return type.toString() + + (type.getUpperBound().getKind() == TypeKind.NULL ? "" + : " extends " + resolveAsString( type.getUpperBound() )) + + (type.getLowerBound().getKind() == TypeKind.NULL ? "" + : " super " + resolveAsString( type.getLowerBound() )); + } + private String returnType() { - final String entityType = annotationMetaEntity.importType(entity); - if ( isReactive() ) { - return annotationMetaEntity.importType(UNI) - + '<' + (returnArgument ? entityType : "Void") + '>'; + if ( returnArgument ) { + return resolveAsString(method.getReturnType()); } else { - return returnArgument ? entityType : "void"; + return isReactive() ? annotationMetaEntity.importType(UNI) + "" : "void"; } } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/TypeUtils.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/TypeUtils.java index ef4117f5b65d..08d0cc1ebebd 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/TypeUtils.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/TypeUtils.java @@ -784,6 +784,11 @@ public static boolean isPrimitive(String paramType) { public static final Set PRIMITIVE_TYPES = Set.of("boolean", "char", "long", "int", "short", "byte", "double", "float"); + public static String resolveTypeName(TypeElement typeElement, Element element, String name) { + final var mirror = resolveTypeMirror( typeElement, element, name ); + return mirror == null ? name : mirror.toString(); + } + public static @Nullable TypeMirror resolveTypeMirror(TypeElement typeElement, Element element, String name) { final var mirrorMap = resolveTypeParameters( typeElement.asType(), element, Map.of(), new HashSet<>() ); return mirrorMap == null ? null : mirrorMap.get( name ); @@ -808,7 +813,7 @@ public static boolean isPrimitive(String paramType) { ? generic.get( 0 ).getBounds().get( 0 ) : typeArguments.get( n ); final var value = mirror.toString(); - map.put( generic.get( n ).toString(), parametersMap.getOrDefault( value, mirror ) ); + map.put( generic.get( n ).asType().toString(), parametersMap.getOrDefault( value, mirror ) ); } if ( typeElement.equals( element ) ) { return map; diff --git a/whats-new.adoc b/whats-new.adoc index 7b7e90bf2220..85b53d181aad 100644 --- a/whats-new.adoc +++ b/whats-new.adoc @@ -1,42 +1,13 @@ -= What's New in 7.1 += What's New in 7.2 :toc: :toclevels: 4 +:version: 7.2 :docsBase: https://docs.jboss.org/hibernate/orm -:versionDocBase: {docsBase}/7.1 +:versionDocBase: {docsBase}/{version} :userGuideBase: {versionDocBase}/userguide/html_single/Hibernate_User_Guide.html :migrationGuide: {versionDocBase}/migration-guide/migration-guide.html -Describes the new features and capabilities added to Hibernate ORM in 7.1. +Describes the new features and capabilities added to Hibernate ORM in {version}. -If migrating from earlier versions, be sure to also check out the link:{migrationGuide}[Migration Guide] for discussion of impactful changes. +IMPORTANT: If migrating from earlier versions, be sure to also check out the link:{migrationGuide}[Migration Guide] for discussion of impactful changes. - -[[resource-discovery]] -== Resource Discovery in SE Environments - -The Jakarta Persistence specification defines the ability for a provider to discover "managed resources" in EE environments, alleviating the application from manually listing all classes and XML files. -However, it defines no such support in SE environments. - -Starting with 7.1, Hibernate now supports this discovery in SE environments, by allowing applications to specify the root URL and zero-or-more "jar" URLs to search. -These URLs correspond to - -* `jakarta.persistence.spi.PersistenceUnitInfo#getPersistenceUnitRootUrl` -* `jakarta.persistence.spi.PersistenceUnitInfo#getJarFileUrls` - -These URLs are searched for managed resources according to the process defined in https://jakarta.ee/specifications/persistence/3.2/jakarta-persistence-spec-3.2#a12305[the specification]. - -[[locking]] -== Locking - -`org.hibernate.Locking` has been introduced to support various aspects of pessimistic locking: - -* `Locking.Scope` is an extension of `jakarta.persistence.PessimisticLockScope` including some Hibernate-specific options. -* `Locking.FollowOn` allows controlling Hibernate's follow-on locking behavior. - -Additionally, we've added `org.hibernate.Timeouts` to help deal with some standard `jakarta.persistence.Timeout` values. - - -[[interceptor-merge]] -== Interceptor and merge - -Support for intercepting `Session#merge` events has been added to Hibernate's `Interceptor`.