From 2bc7fed96ecab48ea6ab98394e0ad93ad829981d Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 1 Oct 2019 14:01:01 +0100 Subject: [PATCH 01/12] HHH-13640 - Add PrepareStatement executed check to LazyToOnesProxyWithoutSubclassesTest --- .../LazyToOnesProxyWithSubclassesTest.java | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesTest.java index da3d77f093..e14705ae9c 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesTest.java @@ -22,6 +22,7 @@ import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.AvailableSettings; import org.hibernate.proxy.HibernateProxy; +import org.hibernate.stat.Statistics; import org.hibernate.testing.FailureExpected; import org.hibernate.testing.TestForIssue; @@ -31,6 +32,7 @@ import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -77,16 +79,52 @@ public class LazyToOnesProxyWithSubclassesTest extends BaseNonConfigCoreFunction inSession( session -> { + final Statistics stats = sessionFactory().getStatistics(); + stats.clear(); final OtherEntity otherEntity = session.get( OtherEntity.class, "test1" ); assertTrue( Hibernate.isPropertyInitialized( otherEntity, "animal" ) ); assertFalse( Hibernate.isInitialized( otherEntity.animal ) ); assertTrue( HibernateProxy.class.isInstance( otherEntity.animal ) ); + + assertEquals( 1, stats.getPrepareStatementCount() ); + Animal animal = session.load( Animal.class, "A Human" ); assertFalse( Hibernate.isInitialized( animal ) ); + assertEquals( 1, stats.getPrepareStatementCount() ); } ); } + @Test + public void testGetInitializeAssociations() { + inTransaction( + session -> { + Human human = new Human( "A Human" ); + OtherEntity otherEntity = new OtherEntity( "test1" ); + otherEntity.animal = human; + + session.persist( human ); + session.persist( otherEntity ); + } + ); + + inSession( + session -> { + final Statistics stats = sessionFactory().getStatistics(); + stats.clear(); + final OtherEntity otherEntity = session.get( OtherEntity.class, "test1" ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "animal" ) ); + assertFalse( Hibernate.isInitialized( otherEntity.animal ) ); + assertEquals( 1, stats.getPrepareStatementCount() ); + + Animal animal = session.get( Animal.class, "A Human" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( 2, stats.getPrepareStatementCount() ); + } + ); + + } + @Test public void testExistingProxyAssociation() { inTransaction( @@ -102,6 +140,8 @@ public class LazyToOnesProxyWithSubclassesTest extends BaseNonConfigCoreFunction inSession( session -> { + final Statistics stats = sessionFactory().getStatistics(); + stats.clear(); final OtherEntity otherEntity = session.get( OtherEntity.class, "test1" ); assertTrue( Hibernate.isPropertyInitialized( otherEntity, "animal" ) ); assertFalse( Hibernate.isInitialized( otherEntity.animal ) ); @@ -109,7 +149,7 @@ public class LazyToOnesProxyWithSubclassesTest extends BaseNonConfigCoreFunction assertTrue( Hibernate.isPropertyInitialized( otherEntity, "primate" ) ); assertFalse( Hibernate.isInitialized( otherEntity.primate ) ); assertTrue( HibernateProxy.class.isInstance( otherEntity.primate ) ); - + assertEquals( 1, stats.getPrepareStatementCount() ); } ); } @@ -131,6 +171,9 @@ public class LazyToOnesProxyWithSubclassesTest extends BaseNonConfigCoreFunction inSession( session -> { + final Statistics stats = sessionFactory().getStatistics(); + stats.clear(); + final OtherEntity otherEntity = session.get( OtherEntity.class, "test1" ); assertTrue( Hibernate.isPropertyInitialized( otherEntity, "animal" ) ); assertFalse( Hibernate.isInitialized( otherEntity.animal ) ); @@ -142,6 +185,8 @@ public class LazyToOnesProxyWithSubclassesTest extends BaseNonConfigCoreFunction assertFalse( Hibernate.isInitialized( otherEntity.human ) ); // TODO: Should otherEntity.human be a narrowed HibernateProxy or // an uninitialized non-HibernateProxy proxy? + assertEquals( 1, stats.getPrepareStatementCount() ); + } ); } @@ -158,7 +203,6 @@ public class LazyToOnesProxyWithSubclassesTest extends BaseNonConfigCoreFunction ); } - @Entity(name = "Animal") @Table(name = "Animal") @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) From b350599442f8ff7e4da7aeb99f43cd9453e30fc0 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 2 Oct 2019 03:05:43 -0500 Subject: [PATCH 02/12] HHH-12858 - integration overrides during JPA bootstrap ought to override all logically related settings - e.g. a datasource passed in the integration overrides map ought to effectively override JDBC-connection settings in `persistence.xml` (and vice-versa) HHH-13432 - Have EntityManagerFactory expose persistence.xml `jta-data-source` element as a `javax.persistence.nonJtaDataSource` property --- .../boot/cfgxml/spi/LoadedConfig.java | 4 +- .../source/internal/hbm/FilterSourceImpl.java | 3 +- .../model/source/internal/hbm/Helper.java | 27 - .../StandardServiceRegistryBuilder.java | 53 +- .../internal/util/NullnessHelper.java | 60 ++ .../EntityManagerFactoryBuilderImpl.java | 661 +++++++++++++----- .../MaskSensitiveInformationTest.java | 4 +- .../connection/DataSourceInjectionTest.java | 116 ++- .../connection/PersistenceUnitInfoImpl.java | 2 +- .../bootstrap/PersistenceUnitInfoTests.java | 54 -- .../jpa/PersistenceUnitInfoTests.java | 105 +++ .../PersistenceUnitOverridesTests.java | 120 ++-- .../testing/jdbc}/DataSourceStub.java | 4 +- 13 files changed, 888 insertions(+), 325 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/internal/util/NullnessHelper.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/PersistenceUnitInfoTests.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitInfoTests.java rename hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/{ => jpa}/PersistenceUnitOverridesTests.java (76%) rename {hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap => hibernate-testing/src/main/java/org/hibernate/testing/jdbc}/DataSourceStub.java (96%) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/cfgxml/spi/LoadedConfig.java b/hibernate-core/src/main/java/org/hibernate/boot/cfgxml/spi/LoadedConfig.java index f2e4f2f696..156f334d09 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/cfgxml/spi/LoadedConfig.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/cfgxml/spi/LoadedConfig.java @@ -47,7 +47,7 @@ public class LoadedConfig { private List mappingReferences; private Map> eventListenerMap; - private LoadedConfig(String sessionFactoryName) { + public LoadedConfig(String sessionFactoryName) { this.sessionFactoryName = sessionFactoryName; } @@ -259,7 +259,7 @@ public class LoadedConfig { } @SuppressWarnings("unchecked") - private void addConfigurationValues(Map configurationValues) { + protected void addConfigurationValues(Map configurationValues) { if ( configurationValues == null ) { return; } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/FilterSourceImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/FilterSourceImpl.java index 6569d928ab..ecdbe1d3f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/FilterSourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/FilterSourceImpl.java @@ -14,6 +14,7 @@ import org.hibernate.boot.MappingException; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmFilterAliasMappingType; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmFilterType; import org.hibernate.boot.model.source.spi.FilterSource; +import org.hibernate.internal.util.NullnessHelper; import org.hibernate.internal.util.StringHelper; /** @@ -63,7 +64,7 @@ public class FilterSourceImpl } } - this.condition = Helper.coalesce( conditionContent, conditionAttribute ); + this.condition = NullnessHelper.coalesce( conditionContent, conditionAttribute ); this.autoAliasInjection = StringHelper.isNotEmpty( explicitAutoAliasInjectionSetting ) ? Boolean.valueOf( explicitAutoAliasInjectionSetting ) : true; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/Helper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/Helper.java index a4249a86ab..50a55e4e52 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/Helper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/Helper.java @@ -123,33 +123,6 @@ public class Helper { return params; } - /** - * Operates like SQL coalesce expression, except empty strings are treated as null. Return the first non-empty value - * - * @param values The list of values. - * @param Generic type of values to coalesce - * - * @return The first non-empty value, or null if all values were empty - */ - public static T coalesce(T... values) { - if ( values == null ) { - return null; - } - for ( T value : values ) { - if ( value != null ) { - if ( String.class.isInstance( value ) ) { - if ( StringHelper.isNotEmpty( (String) value ) ) { - return value; - } - } - else { - return value; - } - } - } - return null; - } - static ToolingHintContext collectToolingHints( ToolingHintContext baseline, ToolingHintContainer toolingHintContainer) { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/StandardServiceRegistryBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/StandardServiceRegistryBuilder.java index be452a6b12..a2c9e22d8e 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/registry/StandardServiceRegistryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/StandardServiceRegistryBuilder.java @@ -36,6 +36,35 @@ import org.hibernate.service.spi.ServiceContributor; * @see org.hibernate.boot.registry.BootstrapServiceRegistryBuilder */ public class StandardServiceRegistryBuilder { + /** + * Intended only for use from {@link org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl}. + * + * Creates a StandardServiceRegistryBuilder specific to the needs of JPA bootstrapping. + * Specifically we ignore properties found in `cfg.xml` files in terms of adding them to + * the builder immediately. EntityManagerFactoryBuilderImpl handles collecting these + * properties itself. + */ + public static StandardServiceRegistryBuilder forJpa(BootstrapServiceRegistry bootstrapServiceRegistry) { + final LoadedConfig loadedConfig = new LoadedConfig( null ) { + @Override + protected void addConfigurationValues(Map configurationValues) { + // here, do nothing + } + }; + return new StandardServiceRegistryBuilder( + bootstrapServiceRegistry, + new HashMap(), + loadedConfig + ) { + @Override + public StandardServiceRegistryBuilder configure(LoadedConfig loadedConfig) { + getAggregatedCfgXml().merge( loadedConfig ); + // super also collects the properties - here we skip that part + return this; + } + }; + } + /** * The default resource name for a hibernate configuration xml file. */ @@ -43,7 +72,7 @@ public class StandardServiceRegistryBuilder { private final Map settings; private final List initiators = standardInitiatorList(); - private final List providedServices = new ArrayList(); + private final List providedServices = new ArrayList<>(); private boolean autoCloseRegistry = true; @@ -67,6 +96,21 @@ public class StandardServiceRegistryBuilder { this( bootstrapServiceRegistry, LoadedConfig.baseline() ); } + /** + * Intended for use exclusively from JPA boot-strapping. + * + * @see #forJpa + */ + private StandardServiceRegistryBuilder( + BootstrapServiceRegistry bootstrapServiceRegistry, + Map settings, + LoadedConfig loadedConfig) { + this.bootstrapServiceRegistry = bootstrapServiceRegistry; + this.configLoader = new ConfigLoader( bootstrapServiceRegistry ); + this.settings = settings; + this.aggregatedCfgXml = loadedConfig; + } + /** * Create a builder with the specified bootstrap services. * @@ -81,6 +125,10 @@ public class StandardServiceRegistryBuilder { this.aggregatedCfgXml = loadedConfigBaseline; } + public ConfigLoader getConfigLoader() { + return configLoader; + } + /** * Intended for internal testing use only!! */ @@ -94,11 +142,12 @@ public class StandardServiceRegistryBuilder { * @return List of standard initiators */ private static List standardInitiatorList() { - final List initiators = new ArrayList( StandardServiceInitiators.LIST.size() ); + final List initiators = new ArrayList<>( StandardServiceInitiators.LIST.size() ); initiators.addAll( StandardServiceInitiators.LIST ); return initiators; } + @SuppressWarnings("unused") public BootstrapServiceRegistry getBootstrapServiceRegistry() { return bootstrapServiceRegistry; } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/NullnessHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/NullnessHelper.java new file mode 100644 index 0000000000..416257e60a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/NullnessHelper.java @@ -0,0 +1,60 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.internal.util; + +import java.util.function.Supplier; + +/** + * @author Steve Ebersole + */ +public class NullnessHelper { + + /** + * Operates like SQL coalesce expression, except empty strings are treated as null. Return the first non-empty value + * + * @param values The list of values. + * @param Generic type of values to coalesce + * + * @return The first non-empty value, or null if all values were empty + */ + public static T coalesce(T... values) { + if ( values == null ) { + return null; + } + for ( T value : values ) { + if ( value != null ) { + if ( String.class.isInstance( value ) ) { + if ( !( (String) value ).isEmpty() ) { + return value; + } + } + else { + return value; + } + } + } + return null; + } + + /** + * Find the first non-null value supplied by the given suppliers + */ + public static T coalesceSuppliedValues(Supplier... valueSuppliers) { + if ( valueSuppliers == null ) { + return null; + } + + for ( Supplier valueSupplier : valueSuppliers ) { + final T value = valueSupplier.get(); + if ( value != null ) { + return value; + } + } + + return null; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java index 94e1dea153..4f5885a473 100644 --- a/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java @@ -27,11 +27,9 @@ import javax.sql.DataSource; import org.hibernate.SessionFactory; import org.hibernate.SessionFactoryObserver; import org.hibernate.boot.CacheRegionDefinition; -import org.hibernate.boot.MetadataBuilder; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.boot.archive.scan.internal.StandardScanOptions; -import org.hibernate.boot.cfgxml.internal.ConfigLoader; import org.hibernate.boot.cfgxml.spi.CfgXmlAccessService; import org.hibernate.boot.cfgxml.spi.LoadedConfig; import org.hibernate.boot.cfgxml.spi.MappingReference; @@ -60,7 +58,9 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.id.factory.spi.MutableIdentifierGeneratorFactory; import org.hibernate.integrator.spi.Integrator; import org.hibernate.internal.EntityManagerMessageLogger; +import org.hibernate.internal.util.NullnessHelper; import org.hibernate.internal.util.StringHelper; +import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder; import org.hibernate.jpa.boot.spi.IntegratorProvider; @@ -73,6 +73,7 @@ import org.hibernate.jpa.spi.IdentifierGeneratorStrategyProvider; import org.hibernate.proxy.EntityNotFoundDelegate; import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl; import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl; +import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder; import org.hibernate.secure.spi.GrantedPermission; import org.hibernate.secure.spi.JaccPermissionDeclarations; import org.hibernate.service.ServiceRegistry; @@ -199,24 +200,31 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil final BootstrapServiceRegistry bsr = buildBootstrapServiceRegistry( integrationSettings, providedClassLoader, providedClassLoaderService); // merge configuration sources and build the "standard" service registry - final StandardServiceRegistryBuilder ssrBuilder = new StandardServiceRegistryBuilder( bsr ); + final StandardServiceRegistryBuilder ssrBuilder = StandardServiceRegistryBuilder.forJpa( bsr ); + final MergedSettings mergedSettings = mergeSettings( persistenceUnit, integrationSettings, ssrBuilder ); + + // flush before completion validation + if ( "true".equals( mergedSettings.configurationValues.get( Environment.FLUSH_BEFORE_COMPLETION ) ) ) { + LOG.definingFlushBeforeCompletionIgnoredInHem( Environment.FLUSH_BEFORE_COMPLETION ); + mergedSettings.configurationValues.put( Environment.FLUSH_BEFORE_COMPLETION, "false" ); + } + + // keep the merged config values for phase-2 this.configurationValues = mergedSettings.getConfigurationValues(); // Build the "standard" service registry ssrBuilder.applySettings( configurationValues ); - configure( ssrBuilder ); + this.standardServiceRegistry = ssrBuilder.build(); - configure( standardServiceRegistry, mergedSettings ); + + configureIdentifierGenerators( standardServiceRegistry ); final MetadataSources metadataSources = new MetadataSources( bsr ); - List attributeConverterDefinitions = populate( - metadataSources, - mergedSettings, - standardServiceRegistry - ); + List attributeConverterDefinitions = applyMappingResources( metadataSources ); + this.metamodelBuilder = (MetadataBuilderImplementor) metadataSources.getMetadataBuilder( standardServiceRegistry ); - populate( metamodelBuilder, mergedSettings, standardServiceRegistry, attributeConverterDefinitions ); + applyMetamodelBuilderSettings( mergedSettings, attributeConverterDefinitions ); applyMetadataBuilderContributor(); @@ -307,7 +315,6 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // temporary! - @SuppressWarnings("unchecked") public Map getConfigurationValues() { return Collections.unmodifiableMap( configurationValues ); } @@ -326,7 +333,10 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil * @param associationManagementEnabled To enable association management feature * @return An enhancement context for classes managed by this EM */ - protected EnhancementContext getEnhancementContext(final boolean dirtyTrackingEnabled, final boolean lazyInitializationEnabled, final boolean associationManagementEnabled ) { + protected EnhancementContext getEnhancementContext( + final boolean dirtyTrackingEnabled, + final boolean lazyInitializationEnabled, + final boolean associationManagementEnabled ) { return new DefaultEnhancementContext() { @Override @@ -454,21 +464,12 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil return bsrBuilder.build(); } - @SuppressWarnings("unchecked") private MergedSettings mergeSettings( PersistenceUnitDescriptor persistenceUnit, Map integrationSettings, StandardServiceRegistryBuilder ssrBuilder) { final MergedSettings mergedSettings = new MergedSettings(); - - // first, apply persistence.xml-defined settings - if ( persistenceUnit.getProperties() != null ) { - mergedSettings.configurationValues.putAll( persistenceUnit.getProperties() ); - } - - mergedSettings.configurationValues.put( PERSISTENCE_UNIT_NAME, persistenceUnit.getName() ); - - final ConfigLoader configLoader = new ConfigLoader( ssrBuilder.getBootstrapServiceRegistry() ); + mergedSettings.processPersistenceUnitDescriptorProperties( persistenceUnit ); // see if the persistence.xml settings named a Hibernate config file.... String cfgXmlResourceName = (String) mergedSettings.configurationValues.remove( CFG_FILE ); @@ -478,35 +479,10 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil } if ( StringHelper.isNotEmpty( cfgXmlResourceName ) ) { - final LoadedConfig loadedCfg = configLoader.loadConfigXmlResource( cfgXmlResourceName ); - processConfigXml( loadedCfg, mergedSettings, ssrBuilder ); + processHibernateConfigXmlResources( ssrBuilder, mergedSettings, cfgXmlResourceName ); } - // finally, apply integration-supplied settings (per JPA spec, integration settings should override other sources) - for ( Map.Entry entry : integrationSettings.entrySet() ) { - if ( entry.getKey() == null ) { - continue; - } - - if ( entry.getValue() == null ) { - mergedSettings.configurationValues.remove( entry.getKey() ); - } - else { - mergedSettings.configurationValues.put( entry.getKey(), entry.getValue() ); - } - } - - if ( !mergedSettings.configurationValues.containsKey( JPA_VALIDATION_MODE ) ) { - if ( persistenceUnit.getValidationMode() != null ) { - mergedSettings.configurationValues.put( JPA_VALIDATION_MODE, persistenceUnit.getValidationMode() ); - } - } - - if ( !mergedSettings.configurationValues.containsKey( JPA_SHARED_CACHE_MODE ) ) { - if ( persistenceUnit.getSharedCacheMode() != null ) { - mergedSettings.configurationValues.put( JPA_SHARED_CACHE_MODE, persistenceUnit.getSharedCacheMode() ); - } - } + normalizeSettings( persistenceUnit, integrationSettings, mergedSettings ); final String jaccContextId = (String) mergedSettings.configurationValues.get( JACC_CONTEXT_ID ); @@ -567,22 +543,446 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil return mergedSettings; } + /** + * Handles normalizing the settings coming from multiple sources, applying proper precedences + */ @SuppressWarnings("unchecked") - private void processConfigXml( - LoadedConfig loadedConfig, - MergedSettings mergedSettings, - StandardServiceRegistryBuilder ssrBuilder) { - if ( ! mergedSettings.configurationValues.containsKey( SESSION_FACTORY_NAME ) ) { - // there is not already a SF-name in the merged settings - final String sfName = loadedConfig.getSessionFactoryName(); - if ( sfName != null ) { - // but the cfg.xml file we are processing named one.. - mergedSettings.configurationValues.put( SESSION_FACTORY_NAME, sfName ); + private void normalizeSettings( + PersistenceUnitDescriptor persistenceUnit, + Map integrationSettings, + MergedSettings mergedSettings) { + // make a copy so we can remove things as we process them + final HashMap integrationSettingsCopy = new HashMap<>( integrationSettings ); + + normalizeConnectionAccessUserAndPass( integrationSettingsCopy, mergedSettings ); + + normalizeTransactionCoordinator( persistenceUnit, integrationSettingsCopy, mergedSettings ); + + normalizeDataAccess( integrationSettingsCopy, mergedSettings, persistenceUnit ); + + // normalize ValidationMode + final Object intgValidationMode = integrationSettingsCopy.remove( JPA_VALIDATION_MODE ); + if ( intgValidationMode != null ) { + mergedSettings.configurationValues.put( JPA_VALIDATION_MODE, intgValidationMode ); + } + else if ( persistenceUnit.getValidationMode() != null ) { + mergedSettings.configurationValues.put( JPA_VALIDATION_MODE, persistenceUnit.getValidationMode() ); + } + + // normalize SharedCacheMode + final Object intgCacheMode = integrationSettingsCopy.remove( JPA_SHARED_CACHE_MODE ); + if ( intgCacheMode != null ) { + mergedSettings.configurationValues.put( JPA_SHARED_CACHE_MODE, intgCacheMode ); + } + else if ( persistenceUnit.getSharedCacheMode() != null ) { + mergedSettings.configurationValues.put( JPA_SHARED_CACHE_MODE, persistenceUnit.getSharedCacheMode() ); + } + + // Apply all "integration overrides" as the last step. By specification, + // these should have precedence. + // + // NOTE that this occurs after the specialized normalize calls above which remove + // any specially-handled settings. + for ( Map.Entry entry : integrationSettingsCopy.entrySet() ) { + if ( entry.getKey() == null ) { + continue; + } + + if ( entry.getValue() == null ) { + mergedSettings.configurationValues.remove( entry.getKey() ); + } + else { + mergedSettings.configurationValues.put( entry.getKey(), entry.getValue() ); + } + } + } + + /** + * Because a DataSource can be secured (requiring Hibernate to pass the USER/PASSWORD when accessing the DataSource) + * we apply precedence to the USER and PASS separately + */ + private void normalizeConnectionAccessUserAndPass( + HashMap integrationSettingsCopy, + MergedSettings mergedSettings) { + //noinspection unchecked + final Object effectiveUser = NullnessHelper.coalesceSuppliedValues( + () -> integrationSettingsCopy.remove( USER ), + () -> integrationSettingsCopy.remove( JPA_JDBC_USER ), + () -> extractPuProperty( persistenceUnit, USER ), + () -> extractPuProperty( persistenceUnit, JPA_JDBC_USER ) + ); + + //noinspection unchecked + final Object effectivePass = NullnessHelper.coalesceSuppliedValues( + () -> integrationSettingsCopy.remove( PASS ), + () -> integrationSettingsCopy.remove( JPA_JDBC_PASSWORD ), + () -> extractPuProperty( persistenceUnit, PASS ), + () -> extractPuProperty( persistenceUnit, JPA_JDBC_PASSWORD ) + ); + + if ( effectiveUser != null || effectivePass != null ) { + applyUserAndPass( effectiveUser, effectivePass, mergedSettings ); + } + } + + private T extractPuProperty(PersistenceUnitDescriptor persistenceUnit, String propertyName) { + //noinspection unchecked + return persistenceUnit.getProperties() == null ? null : (T) persistenceUnit.getProperties().get( propertyName ); + } + + @SuppressWarnings("unchecked") + private void applyUserAndPass(Object effectiveUser, Object effectivePass, MergedSettings mergedSettings) { + if ( effectiveUser != null ) { + mergedSettings.configurationValues.put( USER, effectiveUser ); + mergedSettings.configurationValues.put( JPA_JDBC_USER, effectiveUser ); + } + + if ( effectivePass != null ) { + mergedSettings.configurationValues.put( PASS, effectivePass ); + mergedSettings.configurationValues.put( JPA_JDBC_PASSWORD, effectivePass ); + } + } + + private static final String IS_JTA_TXN_COORD = "local.setting.IS_JTA_TXN_COORD"; + + @SuppressWarnings("unchecked") + private void normalizeTransactionCoordinator( + PersistenceUnitDescriptor persistenceUnit, + HashMap integrationSettingsCopy, + MergedSettings mergedSettings) { + PersistenceUnitTransactionType txnType = null; + + final Object intgTxnType = integrationSettingsCopy.remove( JPA_TRANSACTION_TYPE ); + + if ( intgTxnType != null ) { + txnType = PersistenceUnitTransactionTypeHelper.interpretTransactionType( intgTxnType ); + } + else if ( persistenceUnit.getTransactionType() != null ) { + txnType = persistenceUnit.getTransactionType(); + } + else { + final Object puPropTxnType = mergedSettings.configurationValues.get( JPA_TRANSACTION_TYPE ); + if ( puPropTxnType != null ) { + txnType = PersistenceUnitTransactionTypeHelper.interpretTransactionType( puPropTxnType ); } } - mergedSettings.configurationValues.putAll( loadedConfig.getConfigurationValues() ); - ssrBuilder.configure( loadedConfig ); + if ( txnType == null ) { + // is it more appropriate to have this be based on bootstrap entry point (EE vs SE)? + LOG.debugf( "PersistenceUnitTransactionType not specified - falling back to RESOURCE_LOCAL" ); + txnType = PersistenceUnitTransactionType.RESOURCE_LOCAL; + } + + boolean hasTxStrategy = mergedSettings.configurationValues.containsKey( TRANSACTION_COORDINATOR_STRATEGY ); + final Boolean definiteJtaCoordinator; + + if ( hasTxStrategy ) { + LOG.overridingTransactionStrategyDangerous( TRANSACTION_COORDINATOR_STRATEGY ); + + // see if we can tell whether it is a JTA coordinator + final Object strategy = mergedSettings.configurationValues.get( TRANSACTION_COORDINATOR_STRATEGY ); + if ( strategy instanceof TransactionCoordinatorBuilder ) { + definiteJtaCoordinator = ( (TransactionCoordinatorBuilder) strategy ).isJta(); + } + else { + definiteJtaCoordinator = false; + } + } + else { + if ( txnType == PersistenceUnitTransactionType.JTA ) { + mergedSettings.configurationValues.put( TRANSACTION_COORDINATOR_STRATEGY, JtaTransactionCoordinatorBuilderImpl.class ); + definiteJtaCoordinator = true; + } + else if ( txnType == PersistenceUnitTransactionType.RESOURCE_LOCAL ) { + mergedSettings.configurationValues.put( TRANSACTION_COORDINATOR_STRATEGY, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class ); + definiteJtaCoordinator = false; + } + else { + throw new IllegalStateException( "Could not determine TransactionCoordinator strategy to use" ); + } + } + + mergedSettings.configurationValues.put( IS_JTA_TXN_COORD, definiteJtaCoordinator ); + } + + private void normalizeDataAccess( + HashMap integrationSettingsCopy, + MergedSettings mergedSettings, + PersistenceUnitDescriptor persistenceUnit) { + if ( dataSource != null ) { + applyDataSource( + dataSource, + // we don't explicitly know + null, + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + + if ( integrationSettingsCopy.containsKey( DATASOURCE ) ) { + final Object dataSourceRef = integrationSettingsCopy.remove( DATASOURCE ); + if ( dataSourceRef != null ) { + applyDataSource( + dataSourceRef, + null, + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + } + + if ( integrationSettingsCopy.containsKey( JPA_JTA_DATASOURCE ) ) { + final Object dataSourceRef = integrationSettingsCopy.remove( JPA_JTA_DATASOURCE ); + if ( dataSourceRef != null ) { + applyDataSource( + dataSourceRef, + true, + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + } + + if ( integrationSettingsCopy.containsKey( JPA_NON_JTA_DATASOURCE ) ) { + final Object dataSourceRef = integrationSettingsCopy.remove( JPA_NON_JTA_DATASOURCE ); + + applyDataSource( + dataSourceRef, + false, + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + + if ( integrationSettingsCopy.containsKey( URL ) ) { + // these have precedence over the JPA ones + final Object integrationJdbcUrl = integrationSettingsCopy.get( URL ); + if ( integrationJdbcUrl != null ) { + //noinspection unchecked + applyJdbcSettings( + integrationJdbcUrl, + NullnessHelper.coalesceSuppliedValues( + () -> ConfigurationHelper.getString( DRIVER, integrationSettingsCopy ), + () -> ConfigurationHelper.getString( JPA_JDBC_DRIVER, integrationSettingsCopy ), + () -> ConfigurationHelper.getString( DRIVER, mergedSettings.configurationValues ), + () -> ConfigurationHelper.getString( JPA_JDBC_DRIVER, mergedSettings.configurationValues ) + ), + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + } + + if ( integrationSettingsCopy.containsKey( JPA_JDBC_URL ) ) { + final Object integrationJdbcUrl = integrationSettingsCopy.get( JPA_JDBC_URL ); + + if ( integrationJdbcUrl != null ) { + //noinspection unchecked + applyJdbcSettings( + integrationJdbcUrl, + NullnessHelper.coalesceSuppliedValues( + () -> ConfigurationHelper.getString( JPA_JDBC_DRIVER, integrationSettingsCopy ), + () -> ConfigurationHelper.getString( JPA_JDBC_DRIVER, mergedSettings.configurationValues ) + ), + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + } + + if ( persistenceUnit.getJtaDataSource() != null ) { + applyDataSource( + persistenceUnit.getJtaDataSource(), + true, + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + + if ( persistenceUnit.getNonJtaDataSource() != null ) { + applyDataSource( + persistenceUnit.getNonJtaDataSource(), + false, + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + + if ( mergedSettings.configurationValues.containsKey( URL ) ) { + final Object url = mergedSettings.configurationValues.get( URL ); + + if ( url != null && ( ! ( url instanceof String ) || StringHelper.isNotEmpty( (String) url ) ) ) { + applyJdbcSettings( + url, + ConfigurationHelper.getString( DRIVER, mergedSettings.configurationValues ), + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + } + + if ( mergedSettings.configurationValues.containsKey( JPA_JDBC_URL ) ) { + final Object url = mergedSettings.configurationValues.get( JPA_JDBC_URL ); + + if ( url != null && ( ! ( url instanceof String ) || StringHelper.isNotEmpty( (String) url ) ) ) { + applyJdbcSettings( + url, + ConfigurationHelper.getString( JPA_JDBC_DRIVER, mergedSettings.configurationValues ), + integrationSettingsCopy, + mergedSettings + ); + + // EARLY EXIT!! + return; + } + } + + // any other conditions to account for? + } + + @SuppressWarnings("unchecked") + private void applyDataSource( + Object dataSourceRef, + Boolean useJtaDataSource, + HashMap integrationSettingsCopy, + MergedSettings mergedSettings) { + + // `IS_JTA_TXN_COORD` is a value set during `#normalizeTransactionCoordinator` to indicate whether + // the execution environment "is JTA" as best as it can tell.. + // + // we use this value when JTA was not explicitly specified in regards the DataSource + final boolean isJtaTransactionCoordinator = (boolean) mergedSettings.configurationValues.remove( IS_JTA_TXN_COORD ); + final boolean isJta = useJtaDataSource == null ? isJtaTransactionCoordinator : useJtaDataSource; + + // add to EMF properties (questionable - see HHH-13432) + final String emfKey; + final String inverseEmfKey; + if ( isJta ) { + emfKey = JPA_JTA_DATASOURCE; + inverseEmfKey = JPA_NON_JTA_DATASOURCE; + } + else { + emfKey = JPA_NON_JTA_DATASOURCE; + inverseEmfKey = JPA_JTA_DATASOURCE; + } + mergedSettings.configurationValues.put( emfKey, dataSourceRef ); + + // clear any settings logically overridden by this datasource + cleanUpConfigKeys( + integrationSettingsCopy, + mergedSettings, + inverseEmfKey, + JPA_JDBC_DRIVER, + DRIVER, + JPA_JDBC_URL, + URL + ); + + + // clean-up the entries in the "integration overrides" so they do not get get picked + // up in the general "integration overrides" handling + cleanUpConfigKeys( integrationSettingsCopy, DATASOURCE, JPA_JTA_DATASOURCE, JPA_NON_JTA_DATASOURCE ); + + // add under Hibernate's DATASOURCE setting where the ConnectionProvider will find it + mergedSettings.configurationValues.put( DATASOURCE, dataSourceRef ); + } + + private void cleanUpConfigKeys(HashMap integrationSettingsCopy, MergedSettings mergedSettings, String... keys) { + for ( String key : keys ) { + final Object removedIntgSetting = integrationSettingsCopy.remove( key ); + if ( removedIntgSetting != null ) { + LOG.debugf( "Removed integration override setting [%s] due to normalization", key ); + } + + final Object removedMergedSetting = mergedSettings.configurationValues.remove( key ); + if ( removedMergedSetting != null ) { + LOG.debugf( "Removed merged setting [%s] due to normalization", key ); + } + } + } + + private void cleanUpConfigKeys(Map settings, String... keys) { + for ( String key : keys ) { + settings.remove( key ); + } + } + + @SuppressWarnings("unchecked") + private void applyJdbcSettings( + Object url, + String driver, + HashMap integrationSettingsCopy, + MergedSettings mergedSettings) { + mergedSettings.configurationValues.put( URL, url ); + mergedSettings.configurationValues.put( JPA_JDBC_URL, url ); + + if ( driver != null ) { + mergedSettings.configurationValues.put( DRIVER, driver ); + mergedSettings.configurationValues.put( JPA_JDBC_DRIVER, driver ); + } + else { + mergedSettings.configurationValues.remove( DRIVER ); + mergedSettings.configurationValues.remove( JPA_JDBC_DRIVER ); + } + + // clean up the integration-map values + cleanUpConfigKeys( + integrationSettingsCopy, + DRIVER, + JPA_JDBC_DRIVER, + URL, + JPA_JDBC_URL, + USER, + JPA_JDBC_USER, + PASS, + JPA_JDBC_PASSWORD + ); + + cleanUpConfigKeys( + integrationSettingsCopy, + mergedSettings, + DATASOURCE, + JPA_JTA_DATASOURCE, + JPA_NON_JTA_DATASOURCE + ); + } + + private void processHibernateConfigXmlResources( + StandardServiceRegistryBuilder ssrBuilder, + MergedSettings mergedSettings, + String cfgXmlResourceName) { + final LoadedConfig loadedConfig = ssrBuilder.getConfigLoader().loadConfigXmlResource( cfgXmlResourceName ); + + mergedSettings.processHibernateConfigXmlResources( loadedConfig ); + + ssrBuilder.getAggregatedCfgXml().merge( loadedConfig ); } private GrantedPermission parseJaccConfigEntry(String keyString, String valueString) { @@ -638,90 +1038,7 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil return new CacheRegionDefinition( cacheType, role, usage, region, lazyProperty ); } - private void configure(StandardServiceRegistryBuilder ssrBuilder) { - - applyJdbcConnectionProperties( ssrBuilder ); - applyTransactionProperties( ssrBuilder ); - - // flush before completion validation - if ( "true".equals( configurationValues.get( Environment.FLUSH_BEFORE_COMPLETION ) ) ) { - ssrBuilder.applySetting( Environment.FLUSH_BEFORE_COMPLETION, "false" ); - LOG.definingFlushBeforeCompletionIgnoredInHem( Environment.FLUSH_BEFORE_COMPLETION ); - } - -// final StrategySelector strategySelector = ssrBuilder.getBootstrapServiceRegistry().getService( StrategySelector.class ); -// final Object interceptorSetting = configurationValues.remove( AvailableSettings.SESSION_INTERCEPTOR ); -// if ( interceptorSetting != null ) { -// settings.setSessionInterceptorClass( -// loadSessionInterceptorClass( interceptorSetting, strategySelector ) -// ); -// } - } - - private void applyJdbcConnectionProperties(StandardServiceRegistryBuilder ssrBuilder) { - if ( dataSource != null ) { - ssrBuilder.applySetting( DATASOURCE, dataSource ); - } - else if ( persistenceUnit.getJtaDataSource() != null ) { - if ( ! ssrBuilder.getSettings().containsKey( DATASOURCE ) ) { - ssrBuilder.applySetting( DATASOURCE, persistenceUnit.getJtaDataSource() ); - // HHH-8121 : make the PU-defined value available to EMF.getProperties() - configurationValues.put( JPA_JTA_DATASOURCE, persistenceUnit.getJtaDataSource() ); - } - } - else if ( persistenceUnit.getNonJtaDataSource() != null ) { - if ( ! ssrBuilder.getSettings().containsKey( DATASOURCE ) ) { - ssrBuilder.applySetting( DATASOURCE, persistenceUnit.getNonJtaDataSource() ); - // HHH-8121 : make the PU-defined value available to EMF.getProperties() - configurationValues.put( JPA_NON_JTA_DATASOURCE, persistenceUnit.getNonJtaDataSource() ); - } - } - else { - final String driver = (String) configurationValues.get( JPA_JDBC_DRIVER ); - if ( StringHelper.isNotEmpty( driver ) ) { - ssrBuilder.applySetting( DRIVER, driver ); - } - final String url = (String) configurationValues.get( JPA_JDBC_URL ); - if ( StringHelper.isNotEmpty( url ) ) { - ssrBuilder.applySetting( URL, url ); - } - final String user = (String) configurationValues.get( JPA_JDBC_USER ); - if ( StringHelper.isNotEmpty( user ) ) { - ssrBuilder.applySetting( USER, user ); - } - final String pass = (String) configurationValues.get( JPA_JDBC_PASSWORD ); - if ( StringHelper.isNotEmpty( pass ) ) { - ssrBuilder.applySetting( PASS, pass ); - } - } - } - - private void applyTransactionProperties(StandardServiceRegistryBuilder ssrBuilder) { - PersistenceUnitTransactionType txnType = PersistenceUnitTransactionTypeHelper.interpretTransactionType( - configurationValues.get( JPA_TRANSACTION_TYPE ) - ); - if ( txnType == null ) { - txnType = persistenceUnit.getTransactionType(); - } - if ( txnType == null ) { - // is it more appropriate to have this be based on bootstrap entry point (EE vs SE)? - txnType = PersistenceUnitTransactionType.RESOURCE_LOCAL; - } - boolean hasTxStrategy = configurationValues.containsKey( TRANSACTION_COORDINATOR_STRATEGY ); - if ( hasTxStrategy ) { - LOG.overridingTransactionStrategyDangerous( TRANSACTION_COORDINATOR_STRATEGY ); - } - else { - if ( txnType == PersistenceUnitTransactionType.JTA ) { - ssrBuilder.applySetting( TRANSACTION_COORDINATOR_STRATEGY, JtaTransactionCoordinatorBuilderImpl.class ); - } - else if ( txnType == PersistenceUnitTransactionType.RESOURCE_LOCAL ) { - ssrBuilder.applySetting( TRANSACTION_COORDINATOR_STRATEGY, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class ); - } - } - } - - private void configure(StandardServiceRegistry ssr, MergedSettings mergedSettings) { + private void configureIdentifierGenerators(StandardServiceRegistry ssr) { final StrategySelector strategySelector = ssr.getService( StrategySelector.class ); // apply id generators @@ -743,10 +1060,9 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil } @SuppressWarnings("unchecked") - protected List populate( - MetadataSources metadataSources, - MergedSettings mergedSettings, - StandardServiceRegistry ssr) { + private List applyMappingResources(MetadataSources metadataSources) { + // todo : where in the heck are `org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor.getManagedClassNames` handled?!? + // final ClassLoaderService classLoaderService = ssr.getService( ClassLoaderService.class ); // // // todo : make sure MetadataSources/Metadata are capable of handling duplicate sources @@ -823,12 +1139,10 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil return attributeConverterDefinitions; } - protected void populate( - MetadataBuilder metamodelBuilder, + private void applyMetamodelBuilderSettings( MergedSettings mergedSettings, - StandardServiceRegistry ssr, List attributeConverterDefinitions) { - ( (MetadataBuilderImplementor) metamodelBuilder ).getBootstrapContext().markAsJpaBootstrap(); + metamodelBuilder.getBootstrapContext().markAsJpaBootstrap(); if ( persistenceUnit.getTempClassLoader() != null ) { metamodelBuilder.applyTempClassLoader( persistenceUnit.getTempClassLoader() ); @@ -912,7 +1226,7 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil // Metamodel will clean this up... try { SessionFactoryBuilder sfBuilder = metadata().getSessionFactoryBuilder(); - populate( sfBuilder, standardServiceRegistry ); + populateSfBuilder( sfBuilder, standardServiceRegistry ); SchemaManagementToolCoordinator.process( metadata, standardServiceRegistry, configurationValues, DelayedDropRegistryNotAvailableImpl.INSTANCE @@ -926,10 +1240,10 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil cancel(); } - @SuppressWarnings("unchecked") + @Override public EntityManagerFactory build() { - SessionFactoryBuilder sfBuilder = metadata().getSessionFactoryBuilder(); - populate( sfBuilder, standardServiceRegistry ); + final SessionFactoryBuilder sfBuilder = metadata().getSessionFactoryBuilder(); + populateSfBuilder( sfBuilder, standardServiceRegistry ); try { return sfBuilder.build(); @@ -939,7 +1253,7 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil } } - protected void populate(SessionFactoryBuilder sfBuilder, StandardServiceRegistry ssr) { + protected void populateSfBuilder(SessionFactoryBuilder sfBuilder, StandardServiceRegistry ssr) { final StrategySelector strategySelector = ssr.getService( StrategySelector.class ); @@ -1024,7 +1338,36 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil private Map jaccPermissionsByContextId; private List cacheRegionDefinitions; + /** + * MergedSettings is initialized with hibernate.properties + */ private MergedSettings() { + configurationValues.putAll( Environment.getProperties() ); + } + + public void processPersistenceUnitDescriptorProperties(PersistenceUnitDescriptor persistenceUnit) { + if ( persistenceUnit.getProperties() != null ) { + configurationValues.putAll( persistenceUnit.getProperties() ); + } + + configurationValues.put( PERSISTENCE_UNIT_NAME, persistenceUnit.getName() ); + + } + + public void processHibernateConfigXmlResources(LoadedConfig loadedConfig){ + if ( ! configurationValues.containsKey( SESSION_FACTORY_NAME ) ) { + // there is not already a SF-name in the merged settings + final String sfName = loadedConfig.getSessionFactoryName(); + if ( sfName != null ) { + // but the cfg.xml file we are processing named one.. + configurationValues.put( SESSION_FACTORY_NAME, sfName ); + } + } + else { + // make sure they match? + } + + configurationValues.putAll( loadedConfig.getConfigurationValues() ); } public Map getConfigurationValues() { diff --git a/hibernate-core/src/test/java/org/hibernate/internal/MaskSensitiveInformationTest.java b/hibernate-core/src/test/java/org/hibernate/internal/MaskSensitiveInformationTest.java index 4cd9a4305b..1e0c63982f 100644 --- a/hibernate-core/src/test/java/org/hibernate/internal/MaskSensitiveInformationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/internal/MaskSensitiveInformationTest.java @@ -34,10 +34,8 @@ public class MaskSensitiveInformationTest extends BaseEntityManagerFunctionalTes } @Override - @SuppressWarnings("unchecked") protected void addConfigOptions(Map options) { - options.put( AvailableSettings.JPA_JDBC_USER, options.get( AvailableSettings.USER ) ); - options.put( AvailableSettings.JPA_JDBC_PASSWORD, options.get( AvailableSettings.PASS ) ); + super.addConfigOptions( options ); } @Test diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/DataSourceInjectionTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/DataSourceInjectionTest.java index c34a643b63..8c1125218c 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/DataSourceInjectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/DataSourceInjectionTest.java @@ -9,54 +9,106 @@ package org.hibernate.jpa.test.connection; -import java.io.File; -import javax.persistence.EntityManagerFactory; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; import javax.persistence.PersistenceException; +import javax.sql.DataSource; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.jpa.HibernatePersistenceProvider; +import org.hibernate.jpa.test.Distributor; +import org.hibernate.jpa.test.Item; +import org.hibernate.jpa.test.xml.Light; +import org.hibernate.jpa.test.xml.Lighter; -import org.hibernate.testing.util.ExceptionUtil; +import org.hibernate.testing.util.jpa.PersistenceUnitInfoAdapter; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; /** * @author Emmanuel Bernard */ public class DataSourceInjectionTest { - EntityManagerFactory emf; @Test public void testDatasourceInjection() throws Exception { - File current = new File("."); - File sub = new File(current, "puroot"); - sub.mkdir(); - PersistenceUnitInfoImpl info = new PersistenceUnitInfoImpl( sub.toURI().toURL(), new String[]{} ); + withPuRoot( + puRootUrl -> { + final PersistenceUnitInfoAdapter persistenceUnitInfo = createPuDescriptor( puRootUrl, new FakeDataSource() ); + + // otherwise the FakeDataSourceException will be eaten trying to resolve the Dialect + final Map intgOverrides = Collections.singletonMap( + AvailableSettings.DIALECT, + H2Dialect.class + ); + + final HibernatePersistenceProvider provider = new HibernatePersistenceProvider(); + try ( final SessionFactoryImplementor sf = provider.createContainerEntityManagerFactory( + persistenceUnitInfo, + intgOverrides + ).unwrap( SessionFactoryImplementor.class ) ) { + + try ( final SessionImplementor session = sf.openSession().unwrap( SessionImplementor.class ) ) { + session.createQuery( "select i from Item i" ).list(); + Assert.fail( "Expecting FakeDataSourceException" ); + } + catch (PersistenceException pe) { + try { + throw (RuntimeException) pe.getCause(); + } + catch (FakeDataSourceException fde) { + //success + } + } + catch (FakeDataSourceException fde) { + //success + } + } + } + ); + } + + protected PersistenceUnitInfoAdapter createPuDescriptor(URL puRootUrl, DataSource dataSource) { + return new PersistenceUnitInfoAdapter() { + @Override + public DataSource getNonJtaDataSource() { + return dataSource; + } + + @Override + public URL getPersistenceUnitRootUrl() { + return puRootUrl; + } + + public List getManagedClassNames() { + List classes = new ArrayList<>(); + classes.add( Item.class.getName() ); + classes.add( Distributor.class.getName() ); + classes.add( Light.class.getName() ); + classes.add( Lighter.class.getName() ); + return classes; + } + }; + } + + private void withPuRoot(Consumer puRootUrlConsumer) throws Exception { + // create a temporary directory to serve as the "PU root URL" + final Path puroot = Files.createTempDirectory( "puroot" ); + final URL puRootUrl = puroot.toUri().toURL(); + try { - emf = new HibernatePersistenceProvider().createContainerEntityManagerFactory( info, null ); - try { - emf.createEntityManager().createQuery( "select i from Item i" ).getResultList(); - } - finally { - try { - emf.close(); - } - catch (Exception ignore) { - int i = 0; - } - } - Assert.fail( "FakeDatasource should have been used" ); - } - catch (PersistenceException pe) { - if(emf != null){ - emf.close(); - } - Assert.assertTrue( ExceptionUtil.rootCause( pe ) instanceof FakeDataSourceException ); - } - catch (FakeDataSourceException fde) { - //success + puRootUrlConsumer.accept( puRootUrl ); } finally { - sub.delete(); + Files.deleteIfExists( puroot ); } } } diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/PersistenceUnitInfoImpl.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/PersistenceUnitInfoImpl.java index cec9460c97..8e8ed9c0e2 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/PersistenceUnitInfoImpl.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/connection/PersistenceUnitInfoImpl.java @@ -37,7 +37,7 @@ public class PersistenceUnitInfoImpl implements PersistenceUnitInfo { private URL puRoot; public PersistenceUnitInfoImpl(URL puRoot, String[] mappingFiles) { - this.mappingFiles = new ArrayList( mappingFiles.length ); + this.mappingFiles = new ArrayList<>( mappingFiles.length ); this.mappingFiles.addAll( Arrays.asList( mappingFiles ) ); this.puRoot = puRoot; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/PersistenceUnitInfoTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/PersistenceUnitInfoTests.java deleted file mode 100644 index 3fe098bdb7..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/PersistenceUnitInfoTests.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * License: GNU Lesser General Public License (LGPL), version 2.1 or later - * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html - */ -package org.hibernate.orm.test.bootstrap; - -import java.util.Collections; -import java.util.Map; -import javax.persistence.EntityManagerFactory; -import javax.persistence.spi.PersistenceProvider; -import javax.sql.DataSource; - -import org.hibernate.cfg.AvailableSettings; -import org.hibernate.jpa.HibernatePersistenceProvider; - -import org.hibernate.testing.FailureExpected; -import org.hibernate.testing.TestForIssue; -import org.hibernate.testing.junit4.BaseUnitTestCase; -import org.hibernate.testing.util.jpa.PersistenceUnitInfoAdapter; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -/** - * @author Steve Ebersole - */ -public class PersistenceUnitInfoTests extends BaseUnitTestCase { - @Test - @TestForIssue( jiraKey = "HHH-13432" ) - @FailureExpected( jiraKey = "HHH-13432" ) - public void testJtaDataExposedAsProperty() { - final DataSource puDataSource = new DataSourceStub( "puDataSource" ); - final PersistenceUnitInfoAdapter info = new PersistenceUnitInfoAdapter() { - - @Override - public DataSource getNonJtaDataSource() { - return puDataSource; - } - }; - - final PersistenceProvider provider = new HibernatePersistenceProvider(); - - final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( - info, - Collections.emptyMap() - ); - - final Map properties = emf.getProperties(); - final Object o = properties.get( AvailableSettings.JPA_JTA_DATASOURCE ); - assertEquals( o, puDataSource ); - } -} 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 new file mode 100644 index 0000000000..1c0ca45444 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitInfoTests.java @@ -0,0 +1,105 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.bootstrap.jpa; + +import java.util.Collections; +import java.util.Map; +import javax.persistence.EntityManagerFactory; +import javax.persistence.spi.PersistenceProvider; +import javax.sql.DataSource; + +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.jpa.HibernatePersistenceProvider; +import org.hibernate.testing.jdbc.DataSourceStub; + +import org.hibernate.testing.FailureExpected; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.testing.util.jpa.PersistenceUnitInfoAdapter; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +/** + * @author Steve Ebersole + */ +public class PersistenceUnitInfoTests extends BaseUnitTestCase { + @Test + @TestForIssue( jiraKey = "HHH-13432" ) + public void testNonJtaDataExposedAsProperty() { + final DataSource puDataSource = new DataSourceStub( "puDataSource" ); + final PersistenceUnitInfoAdapter info = new PersistenceUnitInfoAdapter() { + + @Override + public DataSource getNonJtaDataSource() { + return puDataSource; + } + }; + + final PersistenceProvider provider = new HibernatePersistenceProvider(); + + final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( + info, + Collections.emptyMap() + ); + + // first let's check the DataSource used in the EMF... + final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) + .getServiceRegistry() + .getService( ConnectionProvider.class ); + assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); + final DatasourceConnectionProviderImpl dsCp = (DatasourceConnectionProviderImpl) connectionProvider; + assertThat( dsCp.getDataSource(), is( puDataSource ) ); + + // now let's check that it is exposed via the EMF properties + // - note : the spec does not indicate that this should work, but + // it worked this way in previous versions + final Object o = emf.getProperties().get( AvailableSettings.JPA_NON_JTA_DATASOURCE ); + assertThat( o, is( puDataSource ) ); + } + + @Test + @TestForIssue( jiraKey = "HHH-13432" ) + public void testJtaDataExposedAsProperty() { + final DataSource puDataSource = new DataSourceStub( "puDataSource" ); + final PersistenceUnitInfoAdapter info = new PersistenceUnitInfoAdapter() { + + @Override + public DataSource getJtaDataSource() { + return puDataSource; + } + }; + + final PersistenceProvider provider = new HibernatePersistenceProvider(); + + final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( + info, + Collections.emptyMap() + ); + + // first let's check the DataSource used in the EMF... + final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) + .getServiceRegistry() + .getService( ConnectionProvider.class ); + assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); + final DatasourceConnectionProviderImpl dsCp = (DatasourceConnectionProviderImpl) connectionProvider; + assertThat( dsCp.getDataSource(), is( puDataSource ) ); + + // now let's check that it is exposed via the EMF properties + // - again, the spec does not indicate that this should work, but + // it worked this way in previous versions + final Map properties = emf.getProperties(); + final Object o = properties.get( AvailableSettings.JPA_JTA_DATASOURCE ); + assertEquals( puDataSource, o ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/PersistenceUnitOverridesTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitOverridesTests.java similarity index 76% rename from hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/PersistenceUnitOverridesTests.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitOverridesTests.java index 6cc59d058f..a40e0c9868 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/PersistenceUnitOverridesTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/jpa/PersistenceUnitOverridesTests.java @@ -4,8 +4,9 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html */ -package org.hibernate.orm.test.bootstrap; +package org.hibernate.orm.test.bootstrap.jpa; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Properties; @@ -21,18 +22,21 @@ import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.jpa.HibernatePersistenceProvider; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.jdbc.DataSourceStub; + import org.hibernate.testing.FailureExpected; import org.hibernate.testing.env.ConnectionProviderBuilder; import org.hibernate.testing.junit4.BaseUnitTestCase; import org.hibernate.testing.util.jpa.DelegatingPersistenceUnitInfo; import org.hibernate.testing.util.jpa.PersistenceUnitInfoAdapter; -import org.hibernate.testing.util.jpa.PersistenceUnitInfoPropertiesWrapper; import org.junit.Test; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; /** @@ -41,31 +45,19 @@ import static org.hamcrest.MatcherAssert.assertThat; public class PersistenceUnitOverridesTests extends BaseUnitTestCase { @Test - public void testCustomProviderPassingIntegrationJpaJdbcOverrides() { - PersistenceProvider provider = new HibernatePersistenceProvider() { - @Override - public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map integrationOverrides) { - return super.createContainerEntityManagerFactory( - new DelegatingPersistenceUnitInfo( info ) { - @Override - public Properties getProperties() { - // use the "db1" connection settings keyed by the JPA property names (org.hibernate.cfg.AvailableSettings.JPA_JDBC_DRIVER, e.g.) - final Properties properties = new Properties(); - properties.putAll( info.getProperties() ); - properties.putAll( ConnectionProviderBuilder.getJpaConnectionProviderProperties( "db1" ) ); - return properties; - } - }, - integrationOverrides - ); - } - }; + public void testPassingIntegrationJpaJdbcOverrides() { - // however, use the "db2" JPA connection settings which should override the persistence unit values + // the integration overrides say to use the "db2" JPA connection settings (which should override the persistence unit values) final Map integrationOverrides = ConnectionProviderBuilder.getJpaConnectionProviderProperties( "db2" ); - final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( - new PersistenceUnitInfoPropertiesWrapper(), + final EntityManagerFactory emf = new HibernatePersistenceProvider().createContainerEntityManagerFactory( + new PersistenceUnitInfoAdapter() { + @Override + public Properties getProperties() { + // effectively, the `persistence.xml` defines `db1` as the connection settings + return ConnectionProviderBuilder.getJpaConnectionProviderProperties( "db1" ); + } + }, integrationOverrides ); @@ -84,7 +76,45 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase { } @Test - public void testPassingIntegrationJpaJdbcOverridesForJtaDataSourceProperty() { + public void testPassingIntegrationJtaDataSourceOverrideForJpaJdbcSettings() { + final PersistenceUnitInfoAdapter puInfo = new PersistenceUnitInfoAdapter( + ConnectionProviderBuilder.getJpaConnectionProviderProperties( "db2" ) + ); + + final DataSource integrationDataSource = new DataSourceStub( "integrationDataSource" ); + + final HibernatePersistenceProvider provider = new HibernatePersistenceProvider(); + + final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( + puInfo, + Collections.singletonMap( AvailableSettings.JPA_JTA_DATASOURCE, integrationDataSource ) + ); + + // first let's check the DataSource used in the EMF... + final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) + .getServiceRegistry() + .getService( ConnectionProvider.class ); + assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); + final DatasourceConnectionProviderImpl dsCp = (DatasourceConnectionProviderImpl) connectionProvider; + assertThat( dsCp.getDataSource(), is( integrationDataSource ) ); + + // now let's check that it is exposed via the EMF properties + // - note : the spec does not indicate that this should work, but + // it worked this way in previous versions + final Object jtaDs = emf.getProperties().get( AvailableSettings.JPA_JTA_DATASOURCE ); + assertThat( jtaDs, is( integrationDataSource ) ); + + // Additionally, we should have set Hibernate's DATASOURCE setting + final Object hibDs = emf.getProperties().get( AvailableSettings.JPA_JTA_DATASOURCE ); + assertThat( hibDs, is( integrationDataSource ) ); + + // Make sure the non-jta-data-source setting was cleared or otherwise null + final Object nonJtaDs = emf.getProperties().get( AvailableSettings.JPA_NON_JTA_DATASOURCE ); + assertThat( nonJtaDs, nullValue() ); + } + + @Test + public void testPassingIntegrationJpaJdbcOverrideForJtaDataSourceProperty() { PersistenceProvider provider = new HibernatePersistenceProvider() { @Override public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map integrationOverrides) { @@ -103,6 +133,16 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase { puProperties.put( AvailableSettings.JPA_JTA_DATASOURCE, puDataSource ); } + @Override + public DataSource getJtaDataSource() { + return null; + } + + @Override + public DataSource getNonJtaDataSource() { + return null; + } + @Override public Properties getProperties() { return puProperties; @@ -142,11 +182,11 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase { } @Test - @FailureExpected( - jiraKey = "HHH-12858", - message = "Even though the JDBC settings override a DataSource *property*, it" + - " does not override a DataSource defined using the dedicated persistence.xml element" - ) +// @FailureExpected( +// jiraKey = "HHH-12858", +// message = "Even though the JDBC settings override a DataSource *property*, it" + +// " does not override a DataSource defined using the dedicated persistence.xml element" +// ) public void testPassingIntegrationJpaJdbcOverridesForJtaDataSourceElement() { PersistenceProvider provider = new HibernatePersistenceProvider() { @Override @@ -154,11 +194,7 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase { return super.createContainerEntityManagerFactory( new DelegatingPersistenceUnitInfo( info ) { // inject a JPA JTA DataSource setting into the PU - final DataSource puDataSource; - - { - puDataSource = new DataSourceStub( "puDataSource" ); - } + final DataSource puDataSource = new DataSourceStub( "puDataSource" ); @Override public DataSource getJtaDataSource() { @@ -199,11 +235,11 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase { } @Test - @FailureExpected( - jiraKey = "HHH-12858", - message = "So it appears any use of the persistence.xml `jta-data-source` or `non-jta-data-source` " + - "have precedence over integration settings, which is also incorrect" - ) +// @FailureExpected( +// jiraKey = "HHH-12858", +// message = "So it appears any use of the persistence.xml `jta-data-source` or `non-jta-data-source` " + +// "have precedence over integration settings, which is also incorrect" +// ) public void testPassingIntegrationJpaDataSourceOverrideForJtaDataSourceElement() { final DataSource puDataSource = new DataSourceStub( "puDataSource" ); final DataSource integrationDataSource = new DataSourceStub( "integrationDataSource" ); @@ -255,7 +291,7 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase { } @Test - @FailureExpected( jiraKey = "HHH-12858", message = "regression - fix" ) + @TestForIssue( jiraKey = "HHH-13640" ) public void testIntegrationOverridesOfPersistenceXmlDataSource() { // mimics a DataSource defined in the persistence.xml @@ -300,7 +336,7 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase { } @Test - @FailureExpected( jiraKey = "HHH-12858", message = "regression - fix" ) + @TestForIssue( jiraKey = "HHH-13640" ) public void testIntegrationOverridesOfPersistenceXmlDataSourceWithDriverManagerInfo() { // mimics a DataSource defined in the persistence.xml diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/DataSourceStub.java b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/DataSourceStub.java similarity index 96% rename from hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/DataSourceStub.java rename to hibernate-testing/src/main/java/org/hibernate/testing/jdbc/DataSourceStub.java index 708c963113..cb1866a3ad 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/DataSourceStub.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/DataSourceStub.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html */ -package org.hibernate.orm.test.bootstrap; +package org.hibernate.testing.jdbc; import java.io.PrintWriter; import java.sql.Connection; @@ -24,7 +24,7 @@ public class DataSourceStub implements DataSource { private final DriverManagerConnectionProviderImpl connectionProvider; private PrintWriter printWriter; - DataSourceStub(String id) { + public DataSourceStub(String id) { this.id = id; connectionProvider = new DriverManagerConnectionProviderImpl(); connectionProvider.configure( ConnectionProviderBuilder.getConnectionProviderProperties() ); From 00f45951daea6bd0bc872edbdd019b76ce6cef0e Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 3 Oct 2019 23:54:23 -0700 Subject: [PATCH 03/12] HHH-13653 : Test case --- .../proxy/ProxyInitializeAndUpdateTest.java | 359 ++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateTest.java new file mode 100644 index 0000000000..17e523867a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateTest.java @@ -0,0 +1,359 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.Hibernate; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.SessionFactoryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; + +import org.hibernate.testing.FailureExpected; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Gail Badner + */ +@TestForIssue( jiraKey = "HHH-13640" ) +@RunWith(BytecodeEnhancerRunner.class) +public class ProxyInitializeAndUpdateTest extends BaseNonConfigCoreFunctionalTestCase { + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" ); + } + + @Override + protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) { + super.configureSessionFactoryBuilder( sfb ); + sfb.applyStatisticsSupport( true ); + sfb.applySecondLevelCacheSupport( false ); + sfb.applyQueryCacheSupport( false ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + super.applyMetadataSources( sources ); + sources.addAnnotatedClass( Animal.class ); + } + + @Test + public void testInitializeWithGetter() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + animal.color = "green"; + session.persist( animal ); + } + ); + + inTransaction( + session -> { + Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + assertEquals( "female", animal.getSex() ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( 3, animal.getAge() ); + animal.setSex( "other" ); + } + ); + + inSession( + session -> { + Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 3, animal.getAge() ); + assertEquals( "green", animal.getColor() ); + } + ); + } + + @Test + @FailureExpected( jiraKey = "HHH-13653") + public void testInitializeWithSetter() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + animal.color = "green"; + session.persist( animal ); + } + ); + + inTransaction( + session -> { + Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + animal.setSex( "other" ); + } + ); + + inSession( + session -> { + Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( "green", animal.getColor() ); + assertEquals( 3, animal.getAge() ); + } + ); + } + + @Test + public void testMergeUpdatedOntoUninitialized() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + session.persist( animal ); + } + ); + + final Animal animalInitialized = doInHibernate( + this::sessionFactory, + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "female", animal.getSex() ); + assertEquals( 3, animal.getAge() ); + return animal; + } + ); + + animalInitialized.setAge( 4 ); + animalInitialized.setSex( "other" ); + + inTransaction( + session -> { + final Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + session.merge( animalInitialized ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( 4, animal.getAge() ); + assertEquals( "other", animal.getSex() ); + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 4, animal.getAge() ); + } + ); + } + + @Test + public void testMergeUpdatedOntoUpdated() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + session.persist( animal ); + } + ); + + final Animal animalInitialized = doInHibernate( + this::sessionFactory, + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "female", animal.getSex() ); + assertEquals( 3, animal.getAge() ); + return animal; + } + ); + + animalInitialized.setAge( 4 ); + animalInitialized.setSex( "other" ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + animal.setAge( 5 ); + animal.setSex( "male" ); + session.merge( animalInitialized ); + assertEquals( 4, animal.getAge() ); + assertEquals( "other", animal.getSex() ); + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 4, animal.getAge() ); + } + ); + } + + @Test + public void testMergeUninitializedOntoUninitialized() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + session.persist( animal ); + } + ); + + final Animal animalUninitialized = doInHibernate( + this::sessionFactory, + session -> { + final Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + return animal; + } + ); + + inTransaction( + session -> { + final Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + session.merge( animalUninitialized ); + assertFalse( Hibernate.isInitialized( animal ) ); + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "female", animal.getSex() ); + assertEquals( 3, animal.getAge() ); + } + ); + } + + @Test + public void testMergeUninitializedOntoUpdated() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + session.persist( animal ); + } + ); + + final Animal animalUninitialized = doInHibernate( + this::sessionFactory, + session -> { + final Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + return animal; + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + animal.setSex( "other" ); + animal.setAge( 4 ); + session.merge( animalUninitialized ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 4, animal.getAge() ); + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 4, animal.getAge() ); + } + ); + } + + @After + public void cleanUpTestData() { + inTransaction( + session -> { + session.createQuery( "delete from Animal" ).executeUpdate(); + } + ); + } + + @Entity(name = "Animal") + @Table(name = "Animal") + public static class Animal { + + @Id + private String name; + + private int age; + + private String sex; + + private String color; + + public String getName() { + return name; + } + + protected void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + } +} \ No newline at end of file From d95c16e9ab1b3d3b718adb4eb0fa0ede62b9cd95 Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Fri, 4 Oct 2019 19:28:20 -0400 Subject: [PATCH 04/12] HHH-13655 Fix NullPointerException for audited MapKeyEnumerated collections with basic value types --- .../internal/ClassesAuditingData.java | 8 +- .../metadata/CollectionMetadataGenerator.java | 5 +- .../mapkey/MapKeyEnumeratedNonEntityTest.java | 156 ++++++++++++++++++ 3 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/mapkey/MapKeyEnumeratedNonEntityTest.java diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/ClassesAuditingData.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/ClassesAuditingData.java index f427e092dd..46d601450b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/ClassesAuditingData.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/ClassesAuditingData.java @@ -115,8 +115,12 @@ public class ClassesAuditingData { if ( propertyAuditingData.getMapKeyEnumType() != null ) { final String referencedEntityName = MappingTools.getReferencedEntityName( property.getValue() ); - final ClassAuditingData referencedAuditingData = entityNameToAuditingData.get( referencedEntityName ); - addMapEnumeratedKey( property.getValue(), property.getPropertyAccessorName(), referencedAuditingData ); + if ( referencedEntityName != null ) { + // If no entity could be determined, this means the enum type isn't an entity mapping and instead is one + // to a basic type. In this use case, there is nothing special to do. + final ClassAuditingData referencedAuditingData = entityNameToAuditingData.get( referencedEntityName ); + addMapEnumeratedKey( property.getValue(), property.getPropertyAccessorName(), referencedAuditingData ); + } } // HHH-9108 diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/CollectionMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/CollectionMetadataGenerator.java index 0e3bc3bd11..b239396a86 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/CollectionMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/CollectionMetadataGenerator.java @@ -526,8 +526,9 @@ public final class CollectionMetadataGenerator { final IndexedCollection indexedValue = (IndexedCollection) propertyValue; final String mapKey = propertyAuditingData.getMapKey(); final EnumType mapKeyEnumType = propertyAuditingData.getMapKeyEnumType(); - if ( mapKey == null && mapKeyEnumType == null ) { - // This entity doesn't specify a javax.persistence.MapKey. Mapping it to the middle entity. + if ( ( mapKey == null && mapKeyEnumType == null ) || ( mapKeyEnumType != null && referencedEntityName == null ) ) { + // This entity doesn't specify a javax.persistence.MapKey or there is a MapKeyEnumerated but its a non-entity type. + // Mapping it to the middle entity. return addValueToMiddleTable( indexedValue.getIndex(), middleEntityXml, diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/mapkey/MapKeyEnumeratedNonEntityTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/mapkey/MapKeyEnumeratedNonEntityTest.java new file mode 100644 index 0000000000..5d0d65e9c5 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/mapkey/MapKeyEnumeratedNonEntityTest.java @@ -0,0 +1,156 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.envers.test.integration.collection.mapkey; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.CollectionTable; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Id; +import javax.persistence.MapKeyColumn; +import javax.persistence.MapKeyEnumerated; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.envers.test.tools.TestTools; +import org.junit.Test; + +import org.hibernate.testing.TestForIssue; + +import static org.hibernate.envers.test.tools.TestTools.checkCollection; +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; + +/** + * @author Chris Cranford + */ +@TestForIssue(jiraKey = "HHH-13655") +public class MapKeyEnumeratedNonEntityTest extends BaseEnversJPAFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { TestEntity.class }; + } + + @Test + @Priority(10) + public void initData() { + // Revision 1 + doInJPA( this::entityManagerFactory, entityManager -> { + + final TestEntity test = new TestEntity(); + test.setId( 1 ); + test.addMapKeyAssociation( TestEnum.ONE, 1 ); + + entityManager.persist( test ); + } ); + + // Revision 2 + doInJPA( this::entityManagerFactory, entityManager -> { + final TestEntity test = entityManager.find( TestEntity.class, 1 ); + test.addMapKeyAssociation( TestEnum.TWO, 2 ); + + entityManager.merge( test ); + } ); + + // Revision 3 + doInJPA( this::entityManagerFactory, entityManager -> { + final TestEntity test = entityManager.find( TestEntity.class, 1 ); + test.removeMapKeyAssociation( TestEnum.ONE ); + entityManager.merge( test ); + } ); + + // Revision 4 + doInJPA( this::entityManagerFactory, entityManager -> { + final TestEntity test = entityManager.find( TestEntity.class, 1 ); + test.removeMapKeyAssociation( TestEnum.TWO ); + entityManager.merge( test ); + } ); + } + + @Test + public void testRevisionNumberHistory() { + assertEquals( Arrays.asList( 1, 2, 3, 4 ), getAuditReader().getRevisions( TestEntity.class, 1 ) ); + } + + @Test + public void testRevisionHistory() { + + final TestEntity rev1 = getAuditReader().find( TestEntity.class, 1, 1 ); + assertEquals( 1, rev1.getMapEnumMap().size() ); + assertEquals( TestEnum.ONE, rev1.getMapEnumMap().keySet().iterator().next() ); + + final TestEntity rev2 = getAuditReader().find( TestEntity.class, 1, 2 ); + assertEquals( 2, rev2.getMapEnumMap().size() ); + assertEquals( TestTools.makeSet( TestEnum.ONE, TestEnum.TWO ), rev2.getMapEnumMap().keySet() ); + checkCollection( rev2.getMapEnumMap().values(), 1, 2 ); + + final TestEntity rev3 = getAuditReader().find( TestEntity.class, 1, 3 ); + assertEquals( 1, rev3.getMapEnumMap().size() ); + assertEquals( TestTools.makeSet( TestEnum.TWO ), rev3.getMapEnumMap().keySet() ); + checkCollection( rev2.getMapEnumMap().values(), 2 ); + + final TestEntity rev4 = getAuditReader().find( TestEntity.class, 1, 4 ); + assertEquals( 0, rev4.getMapEnumMap().size() ); + } + + public enum TestEnum { + ONE, + TWO + } + + @Entity(name = "TestEntity") + @Audited + public static class TestEntity { + @Id + private Integer id; + + @MapKeyEnumerated(EnumType.STRING) + @ElementCollection + @CollectionTable(name = "test_Entity_enum_items") + @MapKeyColumn(name = "type", length = 20, nullable = false) + private Map mapEnumMap = new HashMap<>(); + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Map getMapEnumMap() { + return mapEnumMap; + } + + public void setMapEnumMap(Map mapEnumMap) { + this.mapEnumMap = mapEnumMap; + } + + @Override + public String toString() { + return "TestEntity{" + + "id=" + id + + ", mapEnumMap=" + mapEnumMap + + '}'; + } + + public void addMapKeyAssociation(TestEnum key, Integer value) { + mapEnumMap.put( key, value ); + } + + public Integer removeMapKeyAssociation(TestEnum key) { + final Integer value = mapEnumMap.get( key ); + mapEnumMap.remove( key ); + return value; + } + } +} From fd72e4f75f73703511b9b47fbcc15a0438a69c2d Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Sat, 5 Oct 2019 22:45:29 -0500 Subject: [PATCH 05/12] HHH-12858 - integration overrides during JPA bootstrap ought to override all logically related settings HHH-13432 - Have EntityManagerFactory expose persistence.xml `jta-data-source` element as a `javax.persistence.nonJtaDataSource` property --- .../jpa/PersistenceUnitOverridesTests.java | 144 +++++++++++++++--- .../orm/test/bootstrap/jpa/hibernate.cfg.xml | 12 ++ 2 files changed, 132 insertions(+), 24 deletions(-) create mode 100644 hibernate-core/src/test/resources/org/hibernate/orm/test/bootstrap/jpa/hibernate.cfg.xml 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 a40e0c9868..89810b67b7 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,6 +16,7 @@ import javax.persistence.spi.PersistenceUnitInfo; import javax.sql.DataSource; import org.hibernate.cfg.AvailableSettings; +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.spi.ConnectionProvider; @@ -23,10 +24,8 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.jpa.HibernatePersistenceProvider; import org.hibernate.testing.TestForIssue; -import org.hibernate.testing.jdbc.DataSourceStub; - -import org.hibernate.testing.FailureExpected; import org.hibernate.testing.env.ConnectionProviderBuilder; +import org.hibernate.testing.jdbc.DataSourceStub; import org.hibernate.testing.junit4.BaseUnitTestCase; import org.hibernate.testing.util.jpa.DelegatingPersistenceUnitInfo; import org.hibernate.testing.util.jpa.PersistenceUnitInfoAdapter; @@ -90,27 +89,32 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase { Collections.singletonMap( AvailableSettings.JPA_JTA_DATASOURCE, integrationDataSource ) ); - // first let's check the DataSource used in the EMF... - final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) - .getServiceRegistry() - .getService( ConnectionProvider.class ); - assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); - final DatasourceConnectionProviderImpl dsCp = (DatasourceConnectionProviderImpl) connectionProvider; - assertThat( dsCp.getDataSource(), is( integrationDataSource ) ); + try { + // first let's check the DataSource used in the EMF... + final ConnectionProvider connectionProvider = emf.unwrap( SessionFactoryImplementor.class ) + .getServiceRegistry() + .getService( ConnectionProvider.class ); + assertThat( connectionProvider, instanceOf( DatasourceConnectionProviderImpl.class ) ); + final DatasourceConnectionProviderImpl dsCp = (DatasourceConnectionProviderImpl) connectionProvider; + assertThat( dsCp.getDataSource(), is( integrationDataSource ) ); - // now let's check that it is exposed via the EMF properties - // - note : the spec does not indicate that this should work, but - // it worked this way in previous versions - final Object jtaDs = emf.getProperties().get( AvailableSettings.JPA_JTA_DATASOURCE ); - assertThat( jtaDs, is( integrationDataSource ) ); + // now let's check that it is exposed via the EMF properties + // - note : the spec does not indicate that this should work, but + // it worked this way in previous versions + final Object jtaDs = emf.getProperties().get( AvailableSettings.JPA_JTA_DATASOURCE ); + assertThat( jtaDs, is( integrationDataSource ) ); - // Additionally, we should have set Hibernate's DATASOURCE setting - final Object hibDs = emf.getProperties().get( AvailableSettings.JPA_JTA_DATASOURCE ); - assertThat( hibDs, is( integrationDataSource ) ); + // Additionally, we should have set Hibernate's DATASOURCE setting + final Object hibDs = emf.getProperties().get( AvailableSettings.JPA_JTA_DATASOURCE ); + assertThat( hibDs, is( integrationDataSource ) ); - // Make sure the non-jta-data-source setting was cleared or otherwise null - final Object nonJtaDs = emf.getProperties().get( AvailableSettings.JPA_NON_JTA_DATASOURCE ); - assertThat( nonJtaDs, nullValue() ); + // Make sure the non-jta-data-source setting was cleared or otherwise null + final Object nonJtaDs = emf.getProperties().get( AvailableSettings.JPA_NON_JTA_DATASOURCE ); + assertThat( nonJtaDs, nullValue() ); + } + finally { + emf.close(); + } } @Test @@ -362,9 +366,101 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase { integrationSettings ); - final SessionFactoryImplementor sessionFactory = emf.unwrap( SessionFactoryImplementor.class ); - final ConnectionProvider connectionProvider = sessionFactory.getServiceRegistry().getService( ConnectionProvider.class ); - assertThat( connectionProvider, instanceOf( DriverManagerConnectionProviderImpl.class ) ); + try { + final SessionFactoryImplementor sessionFactory = emf.unwrap( SessionFactoryImplementor.class ); + final ConnectionProvider connectionProvider = sessionFactory.getServiceRegistry().getService( + ConnectionProvider.class ); + assertThat( connectionProvider, instanceOf( DriverManagerConnectionProviderImpl.class ) ); + } + finally { + emf.close(); + } + } + + @Test + public void testCfgXmlBaseline() { + final PersistenceUnitInfoAdapter info = new PersistenceUnitInfoAdapter() { + private final Properties props = new Properties(); + { + props.put( org.hibernate.jpa.AvailableSettings.CFG_FILE, "org/hibernate/orm/test/bootstrap/jpa/hibernate.cfg.xml" ); + } + + @Override + public Properties getProperties() { + return props; + } + }; + + final PersistenceProvider provider = new HibernatePersistenceProvider(); + + final Map integrationSettings = Collections.emptyMap(); + + final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( + info, + integrationSettings + ); + + try { + assertThat( + emf.getProperties().get( AvailableSettings.DIALECT ), + is( PersistenceUnitDialect.class.getName() ) + ); + assertThat( + emf.unwrap( SessionFactoryImplementor.class ).getJdbcServices().getDialect(), + instanceOf( PersistenceUnitDialect.class ) + ); + } + finally { + emf.close(); + } + } + + @Test + public void testIntegrationOverridesOfCfgXml() { + final PersistenceUnitInfoAdapter info = new PersistenceUnitInfoAdapter() { + private final Properties props = new Properties(); + { + props.put( org.hibernate.jpa.AvailableSettings.CFG_FILE, "org/hibernate/orm/test/bootstrap/jpa/hibernate.cfg.xml" ); + } + + @Override + public Properties getProperties() { + return props; + } + }; + + final PersistenceProvider provider = new HibernatePersistenceProvider(); + + final Map integrationSettings = Collections.singletonMap( + AvailableSettings.DIALECT, + IntegrationDialect.class.getName() + ); + + final EntityManagerFactory emf = provider.createContainerEntityManagerFactory( + info, + integrationSettings + ); + + try { + assertThat( + emf.getProperties().get( AvailableSettings.DIALECT ), + is( IntegrationDialect.class.getName() ) + ); + assertThat( + emf.unwrap( SessionFactoryImplementor.class ).getJdbcServices().getDialect(), + instanceOf( IntegrationDialect.class ) + ); + } + finally { + emf.close(); + } + } + + public static class PersistenceUnitDialect extends Dialect { + } + + @SuppressWarnings("WeakerAccess") + public static class IntegrationDialect extends Dialect { } } diff --git a/hibernate-core/src/test/resources/org/hibernate/orm/test/bootstrap/jpa/hibernate.cfg.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/bootstrap/jpa/hibernate.cfg.xml new file mode 100644 index 0000000000..cd46e82e82 --- /dev/null +++ b/hibernate-core/src/test/resources/org/hibernate/orm/test/bootstrap/jpa/hibernate.cfg.xml @@ -0,0 +1,12 @@ + + + + + org.hibernate.orm.test.bootstrap.jpa.PersistenceUnitOverridesTests$PersistenceUnitDialect + + \ No newline at end of file From ab508d2afacd3aa564d2a71ad067b2e11adebba7 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Sat, 5 Oct 2019 23:03:09 -0500 Subject: [PATCH 06/12] HHH-12858 - integration overrides during JPA bootstrap ought to override all logically related settings HHH-13432 - Have EntityManagerFactory expose persistence.xml `jta-data-source` element as a `javax.persistence.nonJtaDataSource` property --- .../jpa/PersistenceUnitOverridesTests.java | 39 +++++++++++++++++++ .../orm/test/bootstrap/jpa/hibernate.cfg.xml | 2 + 2 files changed, 41 insertions(+) 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 89810b67b7..d9a6bce824 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 @@ -10,11 +10,15 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Properties; +import javax.persistence.Entity; import javax.persistence.EntityManagerFactory; +import javax.persistence.Id; +import javax.persistence.metamodel.EntityType; import javax.persistence.spi.PersistenceProvider; import javax.persistence.spi.PersistenceUnitInfo; import javax.sql.DataSource; +import org.hibernate.cache.spi.access.AccessType; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl; @@ -22,6 +26,7 @@ import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionPro import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.jpa.HibernatePersistenceProvider; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.env.ConnectionProviderBuilder; @@ -409,6 +414,9 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase { emf.unwrap( SessionFactoryImplementor.class ).getJdbcServices().getDialect(), instanceOf( PersistenceUnitDialect.class ) ); + + final EntityType entityMapping = emf.getMetamodel().entity( MappedEntity.class ); + assertThat( entityMapping, notNullValue() ); } finally { emf.close(); @@ -450,6 +458,15 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase { emf.unwrap( SessionFactoryImplementor.class ).getJdbcServices().getDialect(), instanceOf( IntegrationDialect.class ) ); + + final EntityPersister entityMapping = emf.unwrap( SessionFactoryImplementor.class ) + .getMetamodel() + .entityPersister( MappedEntity.class ); + assertThat( entityMapping, notNullValue() ); + assertThat( + entityMapping.getCacheAccessStrategy().getAccessType(), + is( AccessType.READ_ONLY ) + ); } finally { emf.close(); @@ -463,4 +480,26 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase { public static class IntegrationDialect extends Dialect { } + @Entity + public static class MappedEntity { + private Integer id; + private String name; + + @Id + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } } diff --git a/hibernate-core/src/test/resources/org/hibernate/orm/test/bootstrap/jpa/hibernate.cfg.xml b/hibernate-core/src/test/resources/org/hibernate/orm/test/bootstrap/jpa/hibernate.cfg.xml index cd46e82e82..fc2dcdac95 100644 --- a/hibernate-core/src/test/resources/org/hibernate/orm/test/bootstrap/jpa/hibernate.cfg.xml +++ b/hibernate-core/src/test/resources/org/hibernate/orm/test/bootstrap/jpa/hibernate.cfg.xml @@ -8,5 +8,7 @@ org.hibernate.orm.test.bootstrap.jpa.PersistenceUnitOverridesTests$PersistenceUnitDialect + + \ No newline at end of file From 3c1f4356dd3a6e6d88ee4a9442119f262b6dfb97 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Fri, 4 Oct 2019 16:41:07 +0100 Subject: [PATCH 07/12] HHH-13653 Uninitialized entity does not get initialized when a setter is called with enhancement-as-proxy enabled --- .../EnhancementAsProxyLazinessInterceptor.java | 14 +++++++++++--- .../lazy/proxy/ProxyInitializeAndUpdateTest.java | 1 - ...eTestWithLazyLoadingAndInlineDirtyTracking.java | 12 ++++++------ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementAsProxyLazinessInterceptor.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementAsProxyLazinessInterceptor.java index 250b89b9ff..4235a83e4d 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementAsProxyLazinessInterceptor.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementAsProxyLazinessInterceptor.java @@ -6,7 +6,6 @@ */ package org.hibernate.bytecode.enhance.spi.interceptor; -import java.io.Serializable; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -40,6 +39,8 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter private boolean initialized; + private boolean initializeBeforeWrite; + public EnhancementAsProxyLazinessInterceptor( String entityName, Set identifierAttributeNames, @@ -59,6 +60,9 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter final EntityPersister entityPersister = session.getFactory().getMetamodel().entityPersister( entityName ); this.inLineDirtyChecking = entityPersister.getEntityMode() == EntityMode.POJO && SelfDirtinessTracker.class.isAssignableFrom( entityPersister.getMappedClass() ); + // if self-dirty tracking is enabled but DynamicUpdate is not enabled then we need to initialise the entity + // because the pre-computed update statement contains even not dirty properties and so we need all the values + initializeBeforeWrite = !inLineDirtyChecking || !entityPersister.getEntityMetamodel().isDynamicUpdate(); } public EntityKey getEntityKey() { @@ -89,7 +93,7 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter .entityPersister( getEntityName() ); final EntityTuplizer entityTuplizer = entityPersister.getEntityTuplizer(); - if ( inLineDirtyChecking && writtenFieldNames != null && !writtenFieldNames.isEmpty() ) { + if ( writtenFieldNames != null && !writtenFieldNames.isEmpty() ) { // enhancement has dirty-tracking available and at least one attribute was explicitly set @@ -241,7 +245,7 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter return newValue; } - if ( ! inLineDirtyChecking ) { + if ( initializeBeforeWrite ) { // we need to force-initialize the proxy - the fetch group to which the `attributeName` belongs try { forceInitialize( target, attributeName ); @@ -249,6 +253,10 @@ public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInter finally { initialized = true; } + + if ( inLineDirtyChecking ) { + ( (SelfDirtinessTracker) target ).$$_hibernate_trackChange( attributeName ); + } } else { // because of the entity being enhanced with `org.hibernate.engine.spi.SelfDirtinessTracker` diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateTest.java index 17e523867a..bfafecb138 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateTest.java @@ -91,7 +91,6 @@ public class ProxyInitializeAndUpdateTest extends BaseNonConfigCoreFunctionalTes } @Test - @FailureExpected( jiraKey = "HHH-13653") public void testInitializeWithSetter() { inTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoadingAndInlineDirtyTracking.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoadingAndInlineDirtyTracking.java index 15b49622e0..3b0de35da4 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoadingAndInlineDirtyTracking.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoadingAndInlineDirtyTracking.java @@ -115,13 +115,13 @@ public class SimpleUpdateTestWithLazyLoadingAndInlineDirtyTracking extends BaseN MatcherAssert.assertThat( interceptor, instanceOf( EnhancementAsProxyLazinessInterceptor.class ) ); loadedChild.setName( updatedName ); - assertEquals( 0, stats.getPrepareStatementCount() ); + assertEquals( 1, stats.getPrepareStatementCount() ); assertThat( loadedChild.getName(), is( updatedName ) ); - assertEquals( 0, stats.getPrepareStatementCount() ); + assertEquals( 1, stats.getPrepareStatementCount() ); } ); // the UPDATE - assertEquals( 1, stats.getPrepareStatementCount() ); + assertEquals( 2, stats.getPrepareStatementCount() ); doInHibernate( this::sessionFactory, s -> { Child loadedChild = s.load( Child.class, lastChildID ); @@ -143,11 +143,11 @@ public class SimpleUpdateTestWithLazyLoadingAndInlineDirtyTracking extends BaseN Parent parent = new Parent(); parent.setName( parentName ); - assertEquals( 0, stats.getPrepareStatementCount() ); + assertEquals( 1, stats.getPrepareStatementCount() ); loadedChild.setParent( parent ); - assertEquals( 0, stats.getPrepareStatementCount() ); + assertEquals( 1, stats.getPrepareStatementCount() ); assertThat( loadedChild.getParent().getName(), is( parentName ) ); - assertEquals( 0, stats.getPrepareStatementCount() ); + assertEquals( 1, stats.getPrepareStatementCount() ); s.save( parent ); } ); From c712b83955522dba4ef1e79c38a9dc39fc8b252a Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Sun, 6 Oct 2019 20:03:44 -0700 Subject: [PATCH 08/12] HHH-13653 : More test cases --- ...eInlineDirtyTrackingDynamicUpdateTest.java | 364 ++++++++++++++++++ ...alizeAndUpdateInlineDirtyTrackingTest.java | 361 +++++++++++++++++ .../proxy/ProxyInitializeAndUpdateTest.java | 5 +- .../SimpleUpdateTestWithLazyLoading.java | 9 +- 4 files changed, 735 insertions(+), 4 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateInlineDirtyTrackingDynamicUpdateTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateInlineDirtyTrackingTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateInlineDirtyTrackingDynamicUpdateTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateInlineDirtyTrackingDynamicUpdateTest.java new file mode 100644 index 0000000000..3a3981e46b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateInlineDirtyTrackingDynamicUpdateTest.java @@ -0,0 +1,364 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.Hibernate; +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.SessionFactoryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.EnhancementOptions; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Gail Badner + */ +@TestForIssue( jiraKey = "HHH-13640" ) +@RunWith(BytecodeEnhancerRunner.class) +@EnhancementOptions(lazyLoading = true,inlineDirtyChecking = true) +public class ProxyInitializeAndUpdateInlineDirtyTrackingDynamicUpdateTest extends BaseNonConfigCoreFunctionalTestCase { + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" ); + } + + @Override + protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) { + super.configureSessionFactoryBuilder( sfb ); + sfb.applyStatisticsSupport( true ); + sfb.applySecondLevelCacheSupport( false ); + sfb.applyQueryCacheSupport( false ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + super.applyMetadataSources( sources ); + sources.addAnnotatedClass( Animal.class ); + } + + @Test + public void testInitializeWithGetter() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + animal.color = "green"; + session.persist( animal ); + } + ); + + inTransaction( + session -> { + Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + assertEquals( "female", animal.getSex() ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( 3, animal.getAge() ); + animal.setSex( "other" ); + } + ); + + inSession( + session -> { + Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 3, animal.getAge() ); + assertEquals( "green", animal.getColor() ); + } + ); + } + + @Test + public void testInitializeWithSetter() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + animal.color = "green"; + session.persist( animal ); + } + ); + + inTransaction( + session -> { + Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + animal.setSex( "other" ); + // Setting the attribute value should not initialize animal + // with dirty-checking and dynamic-update. + assertFalse( Hibernate.isInitialized( animal ) ); + } + ); + + inSession( + session -> { + Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( "green", animal.getColor() ); + assertEquals( 3, animal.getAge() ); + } + ); + } + + @Test + public void testMergeUpdatedOntoUninitialized() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + session.persist( animal ); + } + ); + + final Animal animalInitialized = doInHibernate( + this::sessionFactory, + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "female", animal.getSex() ); + assertEquals( 3, animal.getAge() ); + return animal; + } + ); + + animalInitialized.setAge( 4 ); + animalInitialized.setSex( "other" ); + + inTransaction( + session -> { + final Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + session.merge( animalInitialized ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( 4, animal.getAge() ); + assertEquals( "other", animal.getSex() ); + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 4, animal.getAge() ); + } + ); + } + + @Test + public void testMergeUpdatedOntoUpdated() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + session.persist( animal ); + } + ); + + final Animal animalInitialized = doInHibernate( + this::sessionFactory, + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "female", animal.getSex() ); + assertEquals( 3, animal.getAge() ); + return animal; + } + ); + + animalInitialized.setAge( 4 ); + animalInitialized.setSex( "other" ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + animal.setAge( 5 ); + animal.setSex( "male" ); + session.merge( animalInitialized ); + assertEquals( 4, animal.getAge() ); + assertEquals( "other", animal.getSex() ); + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 4, animal.getAge() ); + } + ); + } + + @Test + public void testMergeUninitializedOntoUninitialized() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + session.persist( animal ); + } + ); + + final Animal animalUninitialized = doInHibernate( + this::sessionFactory, + session -> { + final Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + return animal; + } + ); + + inTransaction( + session -> { + final Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + session.merge( animalUninitialized ); + assertFalse( Hibernate.isInitialized( animal ) ); + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "female", animal.getSex() ); + assertEquals( 3, animal.getAge() ); + } + ); + } + + @Test + public void testMergeUninitializedOntoUpdated() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + session.persist( animal ); + } + ); + + final Animal animalUninitialized = doInHibernate( + this::sessionFactory, + session -> { + final Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + return animal; + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + animal.setSex( "other" ); + animal.setAge( 4 ); + session.merge( animalUninitialized ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 4, animal.getAge() ); + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 4, animal.getAge() ); + } + ); + } + + @After + public void cleanUpTestData() { + inTransaction( + session -> { + session.createQuery( "delete from Animal" ).executeUpdate(); + } + ); + } + + @Entity(name = "Animal") + @Table(name = "Animal") + @DynamicUpdate + public static class Animal { + + @Id + private String name; + + private int age; + + private String sex; + + private String color; + + public String getName() { + return name; + } + + protected void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateInlineDirtyTrackingTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateInlineDirtyTrackingTest.java new file mode 100644 index 0000000000..67f458b065 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateInlineDirtyTrackingTest.java @@ -0,0 +1,361 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.test.bytecode.enhancement.lazy.proxy; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.Hibernate; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.SessionFactoryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.EnhancementOptions; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Gail Badner + */ +@TestForIssue( jiraKey = "HHH-13640" ) +@RunWith(BytecodeEnhancerRunner.class) +@EnhancementOptions(lazyLoading = true,inlineDirtyChecking = true) +public class ProxyInitializeAndUpdateInlineDirtyTrackingTest extends BaseNonConfigCoreFunctionalTestCase { + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" ); + } + + @Override + protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) { + super.configureSessionFactoryBuilder( sfb ); + sfb.applyStatisticsSupport( true ); + sfb.applySecondLevelCacheSupport( false ); + sfb.applyQueryCacheSupport( false ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + super.applyMetadataSources( sources ); + sources.addAnnotatedClass( Animal.class ); + } + + @Test + public void testInitializeWithGetter() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + animal.color = "green"; + session.persist( animal ); + } + ); + + inTransaction( + session -> { + Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + assertEquals( "female", animal.getSex() ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( 3, animal.getAge() ); + animal.setSex( "other" ); + } + ); + + inSession( + session -> { + Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 3, animal.getAge() ); + assertEquals( "green", animal.getColor() ); + } + ); + } + + @Test + public void testInitializeWithSetter() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + animal.color = "green"; + session.persist( animal ); + } + ); + + inTransaction( + session -> { + Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + animal.setSex( "other" ); + // Setting the attribute value should have initialized animal. + assertTrue( Hibernate.isInitialized( animal ) ); + } + ); + + inSession( + session -> { + Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( "green", animal.getColor() ); + assertEquals( 3, animal.getAge() ); + } + ); + } + + @Test + public void testMergeUpdatedOntoUninitialized() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + session.persist( animal ); + } + ); + + final Animal animalInitialized = doInHibernate( + this::sessionFactory, + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "female", animal.getSex() ); + assertEquals( 3, animal.getAge() ); + return animal; + } + ); + + animalInitialized.setAge( 4 ); + animalInitialized.setSex( "other" ); + + inTransaction( + session -> { + final Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + session.merge( animalInitialized ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( 4, animal.getAge() ); + assertEquals( "other", animal.getSex() ); + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 4, animal.getAge() ); + } + ); + } + + @Test + public void testMergeUpdatedOntoUpdated() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + session.persist( animal ); + } + ); + + final Animal animalInitialized = doInHibernate( + this::sessionFactory, + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "female", animal.getSex() ); + assertEquals( 3, animal.getAge() ); + return animal; + } + ); + + animalInitialized.setAge( 4 ); + animalInitialized.setSex( "other" ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + animal.setAge( 5 ); + animal.setSex( "male" ); + session.merge( animalInitialized ); + assertEquals( 4, animal.getAge() ); + assertEquals( "other", animal.getSex() ); + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 4, animal.getAge() ); + } + ); + } + + @Test + public void testMergeUninitializedOntoUninitialized() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + session.persist( animal ); + } + ); + + final Animal animalUninitialized = doInHibernate( + this::sessionFactory, + session -> { + final Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + return animal; + } + ); + + inTransaction( + session -> { + final Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + session.merge( animalUninitialized ); + assertFalse( Hibernate.isInitialized( animal ) ); + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "female", animal.getSex() ); + assertEquals( 3, animal.getAge() ); + } + ); + } + + @Test + public void testMergeUninitializedOntoUpdated() { + inTransaction( + session -> { + Animal animal = new Animal(); + animal.name = "animal"; + animal.age = 3; + animal.sex = "female"; + session.persist( animal ); + } + ); + + final Animal animalUninitialized = doInHibernate( + this::sessionFactory, + session -> { + final Animal animal = session.load( Animal.class, "animal" ); + assertFalse( Hibernate.isInitialized( animal ) ); + return animal; + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + animal.setSex( "other" ); + animal.setAge( 4 ); + session.merge( animalUninitialized ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 4, animal.getAge() ); + } + ); + + inTransaction( + session -> { + final Animal animal = session.get( Animal.class, "animal" ); + assertTrue( Hibernate.isInitialized( animal ) ); + assertEquals( "other", animal.getSex() ); + assertEquals( 4, animal.getAge() ); + } + ); + } + + @After + public void cleanUpTestData() { + inTransaction( + session -> { + session.createQuery( "delete from Animal" ).executeUpdate(); + } + ); + } + + @Entity(name = "Animal") + @Table(name = "Animal") + public static class Animal { + + @Id + private String name; + + private int age; + + private String sex; + + private String color; + + public String getName() { + return name; + } + + protected void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateTest.java index bfafecb138..562d68bc40 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/ProxyInitializeAndUpdateTest.java @@ -16,9 +16,9 @@ import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.testing.FailureExpected; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.EnhancementOptions; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.junit.After; import org.junit.Test; @@ -34,6 +34,7 @@ import static org.junit.Assert.assertTrue; */ @TestForIssue( jiraKey = "HHH-13640" ) @RunWith(BytecodeEnhancerRunner.class) +@EnhancementOptions(lazyLoading = true) public class ProxyInitializeAndUpdateTest extends BaseNonConfigCoreFunctionalTestCase { @Override protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { @@ -108,6 +109,8 @@ public class ProxyInitializeAndUpdateTest extends BaseNonConfigCoreFunctionalTes Animal animal = session.load( Animal.class, "animal" ); assertFalse( Hibernate.isInitialized( animal ) ); animal.setSex( "other" ); + // Setting the attribute value should have initialized animal. + assertTrue( Hibernate.isInitialized( animal ) ); } ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoading.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoading.java index 6f31d92af6..fa896483fd 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoading.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/SimpleUpdateTestWithLazyLoading.java @@ -25,12 +25,9 @@ import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.PersistentAttributeInterceptable; import org.hibernate.engine.spi.PersistentAttributeInterceptor; -import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.stat.Statistics; @@ -200,8 +197,12 @@ public class SimpleUpdateTestWithLazyLoading extends BaseNonConfigCoreFunctional doInHibernate( this::sessionFactory, s -> { Child loadedChild = s.load( Child.class, lastChildID ); + assertThat( Hibernate.isInitialized( loadedChild ), is( false ) ); assertThat( loadedChild.getName(), is( updatedName ) ); + assertThat( Hibernate.isInitialized( loadedChild ), is( true ) ); + assertThat( Hibernate.isInitialized( loadedChild.getParent() ), is( false ) ); assertThat( loadedChild.getParent().getName(), is( parentName ) ); + assertThat( Hibernate.isInitialized( loadedChild.getParent() ), is( true ) ); } ); } @@ -216,7 +217,9 @@ public class SimpleUpdateTestWithLazyLoading extends BaseNonConfigCoreFunctional assertEquals( 0, stats.getPrepareStatementCount() ); Person relative = new Person(); relative.setName( "Luis" ); + assertThat( Hibernate.isInitialized( loadedChild ), is( false ) ); loadedChild.addRelative( relative ); + assertThat( Hibernate.isInitialized( loadedChild ), is( true ) ); assertEquals( 2, stats.getPrepareStatementCount() ); s.persist( relative ); } ); From cec4228d700cbad63a330380c784776ba5cbf724 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 2 Oct 2019 16:51:37 +0100 Subject: [PATCH 09/12] HHH-13640 - Fix initialization of existing proxy association leaf subclass --- .../internal/DefaultLoadEventListener.java | 7 +---- .../LazyToOnesProxyWithSubclassesTest.java | 26 +++++++++++++++++-- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java index 7b33d86f83..4ffe2512d8 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java @@ -288,12 +288,7 @@ public class DefaultLoadEventListener implements LoadEventListener { LazyInitializer li = ( (HibernateProxy) proxy ).getHibernateLazyInitializer(); if ( li.isUnwrap() ) { - if ( entityMetamodel.hasSubclasses() ) { - LOG.debug( "Ignoring NO_PROXY for to-one association with subclasses to honor laziness" ); - } - else { - return li.getImplementation(); - } + LOG.debug( "Ignoring NO_PROXY to honor laziness" ); } return persistenceContext.narrowProxy( proxy, persister, keyToLoad, null ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesTest.java index e14705ae9c..f19dd3d1cc 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesTest.java @@ -24,7 +24,6 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.proxy.HibernateProxy; import org.hibernate.stat.Statistics; -import org.hibernate.testing.FailureExpected; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; @@ -155,7 +154,6 @@ public class LazyToOnesProxyWithSubclassesTest extends BaseNonConfigCoreFunction } @Test - @FailureExpected( jiraKey = "HHH-13640" ) public void testExistingProxyAssociationLeafSubclass() { inTransaction( session -> { @@ -187,6 +185,12 @@ public class LazyToOnesProxyWithSubclassesTest extends BaseNonConfigCoreFunction // an uninitialized non-HibernateProxy proxy? assertEquals( 1, stats.getPrepareStatementCount() ); + Human human = otherEntity.getHuman(); + human.getName(); + assertEquals( 1, stats.getPrepareStatementCount() ); + + human.getSex(); + assertEquals( 2, stats.getPrepareStatementCount() ); } ); } @@ -239,6 +243,8 @@ public class LazyToOnesProxyWithSubclassesTest extends BaseNonConfigCoreFunction @Table(name = "Human") public static class Human extends Primate { + private String sex; + public Human(String name) { this(); setName( name ); @@ -247,6 +253,14 @@ public class LazyToOnesProxyWithSubclassesTest extends BaseNonConfigCoreFunction protected Human() { // this form used by Hibernate } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex; + } } @Entity(name = "OtherEntity") @@ -279,5 +293,13 @@ public class LazyToOnesProxyWithSubclassesTest extends BaseNonConfigCoreFunction public String getId() { return id; } + + public Human getHuman() { + return human; + } + + public void setHuman(Human human) { + this.human = human; + } } } \ No newline at end of file From d6376834a723f5147ba4152c1d5576fb500b3acb Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Wed, 2 Oct 2019 22:12:19 -0700 Subject: [PATCH 10/12] HHH-13640 : Minor improvement --- .../event/internal/DefaultLoadEventListener.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java index 4ffe2512d8..ede86d11bc 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLoadEventListener.java @@ -241,8 +241,9 @@ public class DefaultLoadEventListener implements LoadEventListener { final EventSource session = event.getSession(); final SessionFactoryImplementor factory = session.getFactory(); + final boolean traceEnabled = LOG.isTraceEnabled(); - if ( LOG.isTraceEnabled() ) { + if ( traceEnabled ) { LOG.tracev( "Loading entity: {0}", MessageHelper.infoString( persister, event.getEntityId(), factory ) @@ -283,11 +284,11 @@ public class DefaultLoadEventListener implements LoadEventListener { final Object proxy = persistenceContext.getProxy( keyToLoad ); if ( proxy != null ) { - LOG.trace( "Entity proxy found in session cache" ); + if( traceEnabled ) { + LOG.trace( "Entity proxy found in session cache" ); + } - LazyInitializer li = ( (HibernateProxy) proxy ).getHibernateLazyInitializer(); - - if ( li.isUnwrap() ) { + if ( LOG.isDebugEnabled() && ( (HibernateProxy) proxy ).getHibernateLazyInitializer().isUnwrap() ) { LOG.debug( "Ignoring NO_PROXY to honor laziness" ); } From 9dd6b8439b9eb932c0ee7d64122bf054175df27c Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 9 Oct 2019 10:24:04 +0100 Subject: [PATCH 11/12] HHH-13660 Reduce allocation costs of IdentityMaps used by ResultSetProcessingContextImpl --- .../ResultSetProcessingContextImpl.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java index e6109e9d2b..a6337065fb 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java @@ -58,6 +58,8 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex private List hydratedEntityRegistrationList; private int nRowsRead = 0; + private Map identifierResolutionContextMap; + /** * Builds a ResultSetProcessingContextImpl * @@ -137,15 +139,22 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex return LockMode.NONE; } - private Map identifierResolutionContextMap; - @Override public EntityReferenceProcessingState getProcessingState(final EntityReference entityReference) { + EntityReferenceProcessingState context; if ( identifierResolutionContextMap == null ) { - identifierResolutionContextMap = new IdentityHashMap<>(); + //The default expected size of IdentityHashMap is 21, which is likely to allocate larger arrays than what is typically necessary. + //Reducing to 5, as a reasonable estimate for typical use: any larger query can better justify the need to resize, + //while single loads shouldn't pay such an high cost. + //This can save a lot of memory as it reduces the internal table of IdentityHashMap from a 64 slot array, to 16 slots: + //that's a 75% memory cost reduction for usage patterns which do many individual loads. + identifierResolutionContextMap = new IdentityHashMap<>(5); + context = null; + } + else { + context = identifierResolutionContextMap.get( entityReference ); } - EntityReferenceProcessingState context = identifierResolutionContextMap.get( entityReference ); if ( context == null ) { context = new EntityReferenceProcessingState() { private boolean wasMissingIdentifier; From 404bc196f7fa07547203286ec6d82f48a1ab13ae Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 9 Oct 2019 10:33:07 +0100 Subject: [PATCH 12/12] HHH-13660 Removing dead parameter: forceFetchLazyAttributes --- .../exec/process/internal/ResultSetProcessingContextImpl.java | 3 --- .../plan/exec/process/internal/ResultSetProcessorImpl.java | 4 ---- 2 files changed, 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java index a6337065fb..791132e67d 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessingContextImpl.java @@ -46,7 +46,6 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex private final AliasResolutionContext aliasResolutionContext; private final boolean readOnly; private final boolean shouldUseOptionalEntityInformation; - private final boolean forceFetchLazyAttributes; private final boolean shouldReturnProxies; private final QueryParameters queryParameters; private final NamedParameterContext namedParameterContext; @@ -74,7 +73,6 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex final AliasResolutionContext aliasResolutionContext, final boolean readOnly, final boolean shouldUseOptionalEntityInformation, - final boolean forceFetchLazyAttributes, final boolean shouldReturnProxies, final QueryParameters queryParameters, final NamedParameterContext namedParameterContext, @@ -85,7 +83,6 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex this.aliasResolutionContext = aliasResolutionContext; this.readOnly = readOnly; this.shouldUseOptionalEntityInformation = shouldUseOptionalEntityInformation; - this.forceFetchLazyAttributes = forceFetchLazyAttributes; this.shouldReturnProxies = shouldReturnProxies; this.queryParameters = queryParameters; this.namedParameterContext = namedParameterContext; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java index 81c32a0fae..7420d0b4d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/ResultSetProcessorImpl.java @@ -99,9 +99,6 @@ public class ResultSetProcessorImpl implements ResultSetProcessor { maxRows = Integer.MAX_VALUE; } - // Handles the "FETCH ALL PROPERTIES" directive in HQL - final boolean forceFetchLazyAttributes = false; - final ResultSetProcessingContextImpl context = new ResultSetProcessingContextImpl( resultSet, session, @@ -109,7 +106,6 @@ public class ResultSetProcessorImpl implements ResultSetProcessor { aliasResolutionContext, readOnly, shouldUseOptionalEntityInstance, - forceFetchLazyAttributes, returnProxies, queryParameters, namedParameterContext,