HHH-13565 Extract Session properties to avoid eager initialization of Map<String,Object> properties

This commit is contained in:
Sanne Grinovero 2019-08-12 21:33:03 +01:00
parent 457e9b61fa
commit e23e6a73e6
2 changed files with 150 additions and 57 deletions

View File

@ -6,6 +6,9 @@
*/ */
package org.hibernate.internal; package org.hibernate.internal;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.LockOptions;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
@ -29,12 +32,27 @@ import org.hibernate.event.spi.RefreshEventListener;
import org.hibernate.event.spi.ReplicateEventListener; import org.hibernate.event.spi.ReplicateEventListener;
import org.hibernate.event.spi.ResolveNaturalIdEventListener; import org.hibernate.event.spi.ResolveNaturalIdEventListener;
import org.hibernate.event.spi.SaveOrUpdateEventListener; import org.hibernate.event.spi.SaveOrUpdateEventListener;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.QueryHints;
import org.hibernate.jpa.internal.util.CacheModeHelper;
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder; import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import javax.persistence.CacheRetrieveMode;
import javax.persistence.CacheStoreMode;
import javax.persistence.PessimisticLockScope;
import static org.hibernate.cfg.AvailableSettings.JPA_LOCK_SCOPE;
import static org.hibernate.cfg.AvailableSettings.JPA_LOCK_TIMEOUT;
import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_RETRIEVE_MODE;
import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_STORE_MODE;
/** /**
* Internal component. * Internal component.
* *
@ -57,6 +75,11 @@ import java.util.Objects;
*/ */
final class FastSessionServices { final class FastSessionServices {
/**
* Default session properties
*/
final Map<String, Object> defaultSessionProperties;
// All session events need to be iterated frequently: // All session events need to be iterated frequently:
private final Iterable<ClearEventListener> clearEventListeners; private final Iterable<ClearEventListener> clearEventListeners;
@ -88,9 +111,12 @@ final class FastSessionServices {
final TransactionCoordinatorBuilder transactionCoordinatorBuilder; final TransactionCoordinatorBuilder transactionCoordinatorBuilder;
final JdbcServices jdbcServices; final JdbcServices jdbcServices;
final boolean isJtaTransactionAccessible; final boolean isJtaTransactionAccessible;
final CacheMode initialSessionCacheMode;
//Private fields: //Private fields:
private final Dialect dialect; private final Dialect dialect;
private final CacheStoreMode defaultCacheStoreMode;
private final CacheRetrieveMode defaultCacheRetrieveMode;
FastSessionServices(SessionFactoryImpl sf) { FastSessionServices(SessionFactoryImpl sf) {
Objects.requireNonNull( sf ); Objects.requireNonNull( sf );
@ -132,6 +158,11 @@ final class FastSessionServices {
this.jdbcServices = sr.getService( JdbcServices.class ); this.jdbcServices = sr.getService( JdbcServices.class );
this.isJtaTransactionAccessible = isTransactionAccessible( sf, transactionCoordinatorBuilder ); this.isJtaTransactionAccessible = isTransactionAccessible( sf, transactionCoordinatorBuilder );
this.defaultSessionProperties = initializeDefaultSessionProperties( sf );
this.defaultCacheStoreMode = determineCacheStoreMode( defaultSessionProperties );
this.defaultCacheRetrieveMode = determineCacheRetrieveMode( defaultSessionProperties );
this.initialSessionCacheMode = CacheModeHelper.interpretCacheMode( defaultCacheStoreMode, defaultCacheRetrieveMode );
} }
Iterable<ClearEventListener> getClearEventListeners() { Iterable<ClearEventListener> getClearEventListeners() {
@ -230,4 +261,67 @@ final class FastSessionServices {
return true; return true;
} }
private static Map<String, Object> initializeDefaultSessionProperties(SessionFactoryImpl sf) {
HashMap<String,Object> p = new HashMap<>();
//Static defaults:
p.putIfAbsent( AvailableSettings.FLUSH_MODE, FlushMode.AUTO.name() );
p.putIfAbsent( JPA_LOCK_SCOPE, PessimisticLockScope.EXTENDED.name() );
p.putIfAbsent( JPA_LOCK_TIMEOUT, LockOptions.WAIT_FOREVER );
p.putIfAbsent( JPA_SHARED_CACHE_RETRIEVE_MODE, CacheModeHelper.DEFAULT_RETRIEVE_MODE );
p.putIfAbsent( JPA_SHARED_CACHE_STORE_MODE, CacheModeHelper.DEFAULT_STORE_MODE );
//Defaults defined by SessionFactory configuration:
final String[] ENTITY_MANAGER_SPECIFIC_PROPERTIES = {
JPA_LOCK_SCOPE,
JPA_LOCK_TIMEOUT,
AvailableSettings.FLUSH_MODE,
JPA_SHARED_CACHE_RETRIEVE_MODE,
JPA_SHARED_CACHE_STORE_MODE,
QueryHints.SPEC_HINT_TIMEOUT
};
final Map<String, Object> properties = sf.getProperties();
for ( String key : ENTITY_MANAGER_SPECIFIC_PROPERTIES ) {
if ( properties.containsKey( key ) ) {
p.put( key, properties.get( key ) );
}
}
return Collections.unmodifiableMap( p );
}
/**
* @param properties the Session properties
* @return either the CacheStoreMode as defined in the Session specific properties, or as defined in the
* properties shared across all sessions (the defaults).
*/
CacheStoreMode getCacheStoreMode(final Map<String, Object> properties) {
if ( properties == null ) {
return this.defaultCacheStoreMode;
}
else {
return determineCacheStoreMode( properties );
}
}
/**
* @param properties the Session properties
* @return either the CacheRetrieveMode as defined in the Session specific properties, or as defined in the
* properties shared across all sessions (the defaults).
*/
CacheRetrieveMode getCacheRetrieveMode(Map<String, Object> properties) {
if ( properties == null ) {
return this.defaultCacheRetrieveMode;
}
else {
return determineCacheRetrieveMode( properties );
}
}
private static CacheRetrieveMode determineCacheRetrieveMode(Map<String, Object> settings) {
return ( CacheRetrieveMode ) settings.get( JPA_SHARED_CACHE_RETRIEVE_MODE );
}
private static CacheStoreMode determineCacheStoreMode(Map<String, Object> settings) {
return ( CacheStoreMode ) settings.get( JPA_SHARED_CACHE_STORE_MODE );
}
} }

View File

@ -192,23 +192,15 @@ import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_STORE_MODE;
* @author Steve Ebersole * @author Steve Ebersole
* @author Brett Meyer * @author Brett Meyer
* @author Chris Cranford * @author Chris Cranford
* @author Sanne Grinovero
*/ */
public final class SessionImpl public final class SessionImpl
extends AbstractSessionImpl extends AbstractSessionImpl
implements EventSource, SessionImplementor, HibernateEntityManagerImplementor { implements EventSource, SessionImplementor, HibernateEntityManagerImplementor {
private static final EntityManagerMessageLogger log = HEMLogging.messageLogger( SessionImpl.class ); private static final EntityManagerMessageLogger log = HEMLogging.messageLogger( SessionImpl.class );
// Defaults to null which means the properties are the default - as defined in FastSessionServices#defaultSessionProperties
private static final String[] ENTITY_MANAGER_SPECIFIC_PROPERTIES = { private Map<String, Object> properties;
JPA_LOCK_SCOPE,
JPA_LOCK_TIMEOUT,
AvailableSettings.FLUSH_MODE,
JPA_SHARED_CACHE_RETRIEVE_MODE,
JPA_SHARED_CACHE_STORE_MODE,
QueryHints.SPEC_HINT_TIMEOUT
};
private Map<String, Object> properties = new HashMap<>();
private transient ActionQueue actionQueue; private transient ActionQueue actionQueue;
private transient StatefulPersistenceContext persistenceContext; private transient StatefulPersistenceContext persistenceContext;
@ -260,67 +252,38 @@ public final class SessionImpl
statistics.openSession(); statistics.openSession();
} }
setLockOptions( this.properties == null ? fastSessionServices.defaultSessionProperties : this.properties, this.lockOptions );
getSession().setCacheMode( fastSessionServices.initialSessionCacheMode );
// NOTE : pulse() already handles auto-join-ability correctly // NOTE : pulse() already handles auto-join-ability correctly
getTransactionCoordinator().pulse(); getTransactionCoordinator().pulse();
setDefaultProperties();
applyProperties();
if ( log.isTraceEnabled() ) { if ( log.isTraceEnabled() ) {
log.tracef( "Opened Session [%s] at timestamp: %s", getSessionIdentifier(), getTimestamp() ); log.tracef( "Opened Session [%s] at timestamp: %s", getSessionIdentifier(), getTimestamp() );
} }
} }
private void setDefaultProperties() {
properties.putIfAbsent( AvailableSettings.FLUSH_MODE, getHibernateFlushMode().name() );
properties.putIfAbsent( JPA_LOCK_SCOPE, PessimisticLockScope.EXTENDED.name() );
properties.putIfAbsent( JPA_LOCK_TIMEOUT, LockOptions.WAIT_FOREVER );
properties.putIfAbsent( JPA_SHARED_CACHE_RETRIEVE_MODE, CacheModeHelper.DEFAULT_RETRIEVE_MODE );
properties.putIfAbsent( JPA_SHARED_CACHE_STORE_MODE, CacheModeHelper.DEFAULT_STORE_MODE );
}
private void applyProperties() {
applyEntityManagerSpecificProperties();
setHibernateFlushMode( ConfigurationHelper.getFlushMode( properties.get( AvailableSettings.FLUSH_MODE ), FlushMode.AUTO ) );
setLockOptions( this.properties, this.lockOptions );
getSession().setCacheMode(
CacheModeHelper.interpretCacheMode(
currentCacheStoreMode(),
currentCacheRetrieveMode()
)
);
}
private void applyEntityManagerSpecificProperties() {
final Map<String, Object> properties = getFactory().getProperties();
for ( String key : ENTITY_MANAGER_SPECIFIC_PROPERTIES ) {
if ( properties.containsKey( key ) ) {
this.properties.put( key, properties.get( key ) );
}
}
}
protected void applyQuerySettingsAndHints(Query query) { protected void applyQuerySettingsAndHints(Query query) {
if ( lockOptions.getLockMode() != LockMode.NONE ) { if ( lockOptions.getLockMode() != LockMode.NONE ) {
query.setLockMode( getLockMode( lockOptions.getLockMode() ) ); query.setLockMode( getLockMode( lockOptions.getLockMode() ) );
} }
final Object queryTimeout; final Object queryTimeout;
if ( ( queryTimeout = properties.get( QueryHints.SPEC_HINT_TIMEOUT ) ) != null ) { if ( ( queryTimeout = getSessionProperty( QueryHints.SPEC_HINT_TIMEOUT ) ) != null ) {
query.setHint( QueryHints.SPEC_HINT_TIMEOUT, queryTimeout ); query.setHint( QueryHints.SPEC_HINT_TIMEOUT, queryTimeout );
} }
final Object lockTimeout; final Object lockTimeout;
if ( ( lockTimeout = properties.get( JPA_LOCK_TIMEOUT ) ) != null ) { if ( ( lockTimeout = getSessionProperty( JPA_LOCK_TIMEOUT ) ) != null ) {
query.setHint( JPA_LOCK_TIMEOUT, lockTimeout ); query.setHint( JPA_LOCK_TIMEOUT, lockTimeout );
} }
} }
private CacheRetrieveMode currentCacheRetrieveMode() { private Object getSessionProperty(final String name) {
return determineCacheRetrieveMode( properties ); if ( properties == null ) {
} return fastSessionServices.defaultSessionProperties.get( name );
}
private CacheStoreMode currentCacheStoreMode() { else {
return determineCacheStoreMode( properties ); return properties.get( name );
}
} }
@Override @Override
@ -3519,20 +3482,20 @@ public final class SessionImpl
} }
if ( retrieveMode == null ) { if ( retrieveMode == null ) {
// use the EM setting // use the EM setting
retrieveMode = determineCacheRetrieveMode( this.properties ); retrieveMode = fastSessionServices.getCacheRetrieveMode( this.properties );
} }
if ( storeMode == null ) { if ( storeMode == null ) {
// use the EM setting // use the EM setting
storeMode = determineCacheStoreMode( this.properties ); storeMode = fastSessionServices.getCacheStoreMode( this.properties );
} }
return CacheModeHelper.interpretCacheMode( storeMode, retrieveMode ); return CacheModeHelper.interpretCacheMode( storeMode, retrieveMode );
} }
private CacheRetrieveMode determineCacheRetrieveMode(Map<String, Object> settings) { private static CacheRetrieveMode determineCacheRetrieveMode(Map<String, Object> settings) {
return ( CacheRetrieveMode ) settings.get( JPA_SHARED_CACHE_RETRIEVE_MODE ); return ( CacheRetrieveMode ) settings.get( JPA_SHARED_CACHE_RETRIEVE_MODE );
} }
private CacheStoreMode determineCacheStoreMode(Map<String, Object> settings) { private static CacheStoreMode determineCacheStoreMode(Map<String, Object> settings) {
return ( CacheStoreMode ) settings.get( JPA_SHARED_CACHE_STORE_MODE ); return ( CacheStoreMode ) settings.get( JPA_SHARED_CACHE_STORE_MODE );
} }
@ -3662,12 +3625,48 @@ public final class SessionImpl
return; return;
} }
if ( propertyName == null ) {
log.warnf( "Property having key null is illegal; value won't be set." );
return;
}
//Store property for future reference:
if ( properties == null ) {
properties = computeCurrentSessionProperties();
}
properties.put( propertyName, value ); properties.put( propertyName, value );
applyProperties();
//now actually update settings, if it's any of these which have a direct impact on this Session state:
if ( AvailableSettings.FLUSH_MODE.equals( propertyName ) ) {
setHibernateFlushMode( ConfigurationHelper.getFlushMode( value, FlushMode.AUTO ) );
}
else if ( JPA_LOCK_SCOPE.equals( propertyName ) || JPA_LOCK_TIMEOUT.equals( propertyName ) ) {
setLockOptions( properties, this.lockOptions );
}
else if ( JPA_SHARED_CACHE_RETRIEVE_MODE.equals( propertyName ) || JPA_SHARED_CACHE_STORE_MODE.equals( propertyName ) ) {
getSession().setCacheMode(
CacheModeHelper.interpretCacheMode(
determineCacheStoreMode( properties ),
determineCacheRetrieveMode( properties )
)
);
}
}
private Map<String, Object> computeCurrentSessionProperties() {
final HashMap<String, Object> map = new HashMap<>( fastSessionServices.defaultSessionProperties );
//The FLUSH_MODE is always set at Session creation time, so it needs special treatment to not eagerly initialize this Map:
map.put( AvailableSettings.FLUSH_MODE, getHibernateFlushMode().name() );
return map;
} }
@Override @Override
public Map<String, Object> getProperties() { public Map<String, Object> getProperties() {
if ( properties == null ) {
properties = computeCurrentSessionProperties();
}
return Collections.unmodifiableMap( properties ); return Collections.unmodifiableMap( properties );
} }