HHH-5184 - Create a ConnectionAcquisitionMode as corollary to ConnectionReleaseMode
This commit is contained in:
parent
b1d1a3c143
commit
abace003e6
|
@ -9,9 +9,6 @@ package org.hibernate;
|
||||||
/**
|
/**
|
||||||
* Indicates the manner in which JDBC Connections should be acquired. Inverse to
|
* Indicates the manner in which JDBC Connections should be acquired. Inverse to
|
||||||
* {@link org.hibernate.ConnectionReleaseMode}.
|
* {@link org.hibernate.ConnectionReleaseMode}.
|
||||||
* <p/>
|
|
||||||
* NOTE : Not yet used. The only current behavior is the legacy behavior, which is
|
|
||||||
* {@link #AS_NEEDED}.
|
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
@ -23,11 +20,16 @@ public enum ConnectionAcquisitionMode {
|
||||||
*/
|
*/
|
||||||
IMMEDIATELY,
|
IMMEDIATELY,
|
||||||
/**
|
/**
|
||||||
* The legacy behavior. A Connection is only acquired when (if_) it is actually needed.
|
* The legacy behavior. A Connection is only acquired when (if) it is actually needed.
|
||||||
*/
|
*/
|
||||||
AS_NEEDED,
|
AS_NEEDED;
|
||||||
/**
|
|
||||||
* Not sure yet tbh :)
|
public static ConnectionAcquisitionMode interpret(String value) {
|
||||||
*/
|
if ( value != null
|
||||||
DEFAULT
|
&& ( "immediate".equalsIgnoreCase( value ) || "immediately".equalsIgnoreCase( value ) ) ) {
|
||||||
|
return IMMEDIATELY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AS_NEEDED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.hibernate.dialect.function.SQLFunction;
|
||||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||||
import org.hibernate.loader.BatchFetchStyle;
|
import org.hibernate.loader.BatchFetchStyle;
|
||||||
import org.hibernate.proxy.EntityNotFoundDelegate;
|
import org.hibernate.proxy.EntityNotFoundDelegate;
|
||||||
|
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
||||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||||
import org.hibernate.tuple.entity.EntityTuplizer;
|
import org.hibernate.tuple.entity.EntityTuplizer;
|
||||||
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
||||||
|
@ -622,6 +623,20 @@ public interface SessionFactoryBuilder {
|
||||||
*/
|
*/
|
||||||
public SessionFactoryBuilder applyJdbcFetchSize(int size);
|
public SessionFactoryBuilder applyJdbcFetchSize(int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the specified handling mode for JDBC connections
|
||||||
|
*
|
||||||
|
* @param connectionHandlingMode The handling mode to apply
|
||||||
|
*
|
||||||
|
* @return {@code this}, for method chaining
|
||||||
|
*
|
||||||
|
* @see org.hibernate.cfg.AvailableSettings#ACQUIRE_CONNECTIONS
|
||||||
|
* @see org.hibernate.cfg.AvailableSettings#RELEASE_CONNECTIONS
|
||||||
|
* @see org.hibernate.ConnectionAcquisitionMode
|
||||||
|
* @see ConnectionReleaseMode
|
||||||
|
*/
|
||||||
|
SessionFactoryBuilder applyConnectionHandlingMode(PhysicalConnectionHandlingMode connectionHandlingMode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply a ConnectionReleaseMode.
|
* Apply a ConnectionReleaseMode.
|
||||||
*
|
*
|
||||||
|
@ -630,8 +645,11 @@ public interface SessionFactoryBuilder {
|
||||||
* @return {@code this}, for method chaining
|
* @return {@code this}, for method chaining
|
||||||
*
|
*
|
||||||
* @see org.hibernate.cfg.AvailableSettings#RELEASE_CONNECTIONS
|
* @see org.hibernate.cfg.AvailableSettings#RELEASE_CONNECTIONS
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link #applyConnectionHandlingMode} instead
|
||||||
*/
|
*/
|
||||||
public SessionFactoryBuilder applyConnectionReleaseMode(ConnectionReleaseMode connectionReleaseMode);
|
@Deprecated
|
||||||
|
SessionFactoryBuilder applyConnectionReleaseMode(ConnectionReleaseMode connectionReleaseMode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should Hibernate apply comments to SQL it generates?
|
* Should Hibernate apply comments to SQL it generates?
|
||||||
|
|
|
@ -13,6 +13,7 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.ConnectionAcquisitionMode;
|
||||||
import org.hibernate.ConnectionReleaseMode;
|
import org.hibernate.ConnectionReleaseMode;
|
||||||
import org.hibernate.CustomEntityDirtinessStrategy;
|
import org.hibernate.CustomEntityDirtinessStrategy;
|
||||||
import org.hibernate.EmptyInterceptor;
|
import org.hibernate.EmptyInterceptor;
|
||||||
|
@ -48,6 +49,7 @@ import org.hibernate.internal.SessionFactoryImpl;
|
||||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||||
import org.hibernate.loader.BatchFetchStyle;
|
import org.hibernate.loader.BatchFetchStyle;
|
||||||
import org.hibernate.proxy.EntityNotFoundDelegate;
|
import org.hibernate.proxy.EntityNotFoundDelegate;
|
||||||
|
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
||||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||||
import org.hibernate.resource.transaction.TransactionCoordinatorBuilder;
|
import org.hibernate.resource.transaction.TransactionCoordinatorBuilder;
|
||||||
import org.hibernate.service.spi.ServiceRegistryImplementor;
|
import org.hibernate.service.spi.ServiceRegistryImplementor;
|
||||||
|
@ -55,6 +57,7 @@ import org.hibernate.tuple.entity.EntityTuplizer;
|
||||||
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import static org.hibernate.cfg.AvailableSettings.ACQUIRE_CONNECTIONS;
|
||||||
import static org.hibernate.cfg.AvailableSettings.AUTO_CLOSE_SESSION;
|
import static org.hibernate.cfg.AvailableSettings.AUTO_CLOSE_SESSION;
|
||||||
import static org.hibernate.cfg.AvailableSettings.AUTO_EVICT_COLLECTION_CACHE;
|
import static org.hibernate.cfg.AvailableSettings.AUTO_EVICT_COLLECTION_CACHE;
|
||||||
import static org.hibernate.cfg.AvailableSettings.AUTO_SESSION_EVENTS_LISTENER;
|
import static org.hibernate.cfg.AvailableSettings.AUTO_SESSION_EVENTS_LISTENER;
|
||||||
|
@ -411,9 +414,26 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionFactoryBuilder applyConnectionHandlingMode(PhysicalConnectionHandlingMode connectionHandlingMode) {
|
||||||
|
this.options.connectionHandlingMode = connectionHandlingMode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SessionFactoryBuilder applyConnectionReleaseMode(ConnectionReleaseMode connectionReleaseMode) {
|
public SessionFactoryBuilder applyConnectionReleaseMode(ConnectionReleaseMode connectionReleaseMode) {
|
||||||
this.options.connectionReleaseMode = connectionReleaseMode;
|
if ( this.options.connectionHandlingMode == null ) {
|
||||||
|
this.options.connectionHandlingMode = PhysicalConnectionHandlingMode.interpret(
|
||||||
|
ConnectionAcquisitionMode.AS_NEEDED,
|
||||||
|
connectionReleaseMode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.options.connectionHandlingMode = PhysicalConnectionHandlingMode.interpret(
|
||||||
|
this.options.connectionHandlingMode.getAcquisitionMode(),
|
||||||
|
connectionReleaseMode
|
||||||
|
);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,7 +546,7 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
|
||||||
private Integer jdbcFetchSize;
|
private Integer jdbcFetchSize;
|
||||||
private boolean scrollableResultSetsEnabled;
|
private boolean scrollableResultSetsEnabled;
|
||||||
private boolean commentsEnabled;
|
private boolean commentsEnabled;
|
||||||
private ConnectionReleaseMode connectionReleaseMode;
|
private PhysicalConnectionHandlingMode connectionHandlingMode;
|
||||||
private boolean wrapResultSetsEnabled;
|
private boolean wrapResultSetsEnabled;
|
||||||
|
|
||||||
private Map<String, SQLFunction> sqlFunctions;
|
private Map<String, SQLFunction> sqlFunctions;
|
||||||
|
@ -684,14 +704,30 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
|
||||||
);
|
);
|
||||||
this.jdbcFetchSize = ConfigurationHelper.getInteger( STATEMENT_FETCH_SIZE, configurationSettings );
|
this.jdbcFetchSize = ConfigurationHelper.getInteger( STATEMENT_FETCH_SIZE, configurationSettings );
|
||||||
|
|
||||||
|
final ConnectionAcquisitionMode connectionAcquisitionMode = ConnectionAcquisitionMode.interpret(
|
||||||
|
ConfigurationHelper.getString(
|
||||||
|
ACQUIRE_CONNECTIONS,
|
||||||
|
configurationSettings,
|
||||||
|
ConnectionAcquisitionMode.AS_NEEDED.name()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
final ConnectionReleaseMode connectionReleaseMode;
|
||||||
final String releaseModeName = ConfigurationHelper.getString( RELEASE_CONNECTIONS, configurationSettings, "auto" );
|
final String releaseModeName = ConfigurationHelper.getString( RELEASE_CONNECTIONS, configurationSettings, "auto" );
|
||||||
if ( "auto".equals( releaseModeName ) ) {
|
if ( "auto".equals( releaseModeName ) ) {
|
||||||
this.connectionReleaseMode = serviceRegistry.getService( TransactionCoordinatorBuilder.class )
|
// nothing was specified (or someone happened to configure the "magic" value)
|
||||||
.getDefaultConnectionReleaseMode();
|
if ( connectionAcquisitionMode == ConnectionAcquisitionMode.IMMEDIATELY ) {
|
||||||
|
connectionReleaseMode = ConnectionReleaseMode.ON_CLOSE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
connectionReleaseMode = serviceRegistry.getService( TransactionCoordinatorBuilder.class )
|
||||||
|
.getDefaultConnectionReleaseMode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
connectionReleaseMode = ConnectionReleaseMode.parse( releaseModeName );
|
connectionReleaseMode = ConnectionReleaseMode.parse( releaseModeName );
|
||||||
}
|
}
|
||||||
|
this.connectionHandlingMode = PhysicalConnectionHandlingMode.interpret( connectionAcquisitionMode, connectionReleaseMode );
|
||||||
|
|
||||||
this.commentsEnabled = ConfigurationHelper.getBoolean( USE_SQL_COMMENTS, configurationSettings );
|
this.commentsEnabled = ConfigurationHelper.getBoolean( USE_SQL_COMMENTS, configurationSettings );
|
||||||
|
|
||||||
|
@ -928,9 +964,14 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
|
||||||
return jdbcFetchSize;
|
return jdbcFetchSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode() {
|
||||||
|
return connectionHandlingMode;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConnectionReleaseMode getConnectionReleaseMode() {
|
public ConnectionReleaseMode getConnectionReleaseMode() {
|
||||||
return connectionReleaseMode;
|
return getPhysicalConnectionHandlingMode().getReleaseMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1194,9 +1235,14 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
|
||||||
return options.getJdbcFetchSize();
|
return options.getJdbcFetchSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode() {
|
||||||
|
return options.getPhysicalConnectionHandlingMode();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConnectionReleaseMode getConnectionReleaseMode() {
|
public ConnectionReleaseMode getConnectionReleaseMode() {
|
||||||
return options.getConnectionReleaseMode();
|
return getPhysicalConnectionHandlingMode().getReleaseMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.boot.internal;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.ConnectionAcquisitionMode;
|
||||||
import org.hibernate.ConnectionReleaseMode;
|
import org.hibernate.ConnectionReleaseMode;
|
||||||
import org.hibernate.CustomEntityDirtinessStrategy;
|
import org.hibernate.CustomEntityDirtinessStrategy;
|
||||||
import org.hibernate.EntityMode;
|
import org.hibernate.EntityMode;
|
||||||
|
@ -27,6 +28,7 @@ import org.hibernate.dialect.function.SQLFunction;
|
||||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||||
import org.hibernate.loader.BatchFetchStyle;
|
import org.hibernate.loader.BatchFetchStyle;
|
||||||
import org.hibernate.proxy.EntityNotFoundDelegate;
|
import org.hibernate.proxy.EntityNotFoundDelegate;
|
||||||
|
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
||||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||||
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
||||||
|
|
||||||
|
@ -108,7 +110,7 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
|
||||||
private final Integer jdbcFetchSize;
|
private final Integer jdbcFetchSize;
|
||||||
private final boolean scrollableResultSetsEnabled;
|
private final boolean scrollableResultSetsEnabled;
|
||||||
private final boolean commentsEnabled;
|
private final boolean commentsEnabled;
|
||||||
private final ConnectionReleaseMode connectionReleaseMode;
|
private final PhysicalConnectionHandlingMode physicalConnectionHandlingMode;
|
||||||
private final boolean wrapResultSetsEnabled;
|
private final boolean wrapResultSetsEnabled;
|
||||||
|
|
||||||
private final Map<String, SQLFunction> sqlFunctions;
|
private final Map<String, SQLFunction> sqlFunctions;
|
||||||
|
@ -168,7 +170,7 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
|
||||||
this.autoEvictCollectionCache = state.isAutoEvictCollectionCache();
|
this.autoEvictCollectionCache = state.isAutoEvictCollectionCache();
|
||||||
|
|
||||||
this.schemaAutoTooling = state.getSchemaAutoTooling();
|
this.schemaAutoTooling = state.getSchemaAutoTooling();
|
||||||
this.connectionReleaseMode = state.getConnectionReleaseMode();
|
this.physicalConnectionHandlingMode = state.getPhysicalConnectionHandlingMode();
|
||||||
this.getGeneratedKeysEnabled = state.isGetGeneratedKeysEnabled();
|
this.getGeneratedKeysEnabled = state.isGetGeneratedKeysEnabled();
|
||||||
this.jdbcBatchSize = state.getJdbcBatchSize();
|
this.jdbcBatchSize = state.getJdbcBatchSize();
|
||||||
this.jdbcBatchVersionedData = state.isJdbcBatchVersionedData();
|
this.jdbcBatchVersionedData = state.isJdbcBatchVersionedData();
|
||||||
|
@ -408,9 +410,14 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
|
||||||
return jdbcFetchSize;
|
return jdbcFetchSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode() {
|
||||||
|
return physicalConnectionHandlingMode;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConnectionReleaseMode getConnectionReleaseMode() {
|
public ConnectionReleaseMode getConnectionReleaseMode() {
|
||||||
return connectionReleaseMode;
|
return physicalConnectionHandlingMode.getReleaseMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.boot.internal;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.ConnectionAcquisitionMode;
|
||||||
import org.hibernate.ConnectionReleaseMode;
|
import org.hibernate.ConnectionReleaseMode;
|
||||||
import org.hibernate.CustomEntityDirtinessStrategy;
|
import org.hibernate.CustomEntityDirtinessStrategy;
|
||||||
import org.hibernate.EntityMode;
|
import org.hibernate.EntityMode;
|
||||||
|
@ -26,6 +27,7 @@ import org.hibernate.dialect.function.SQLFunction;
|
||||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||||
import org.hibernate.loader.BatchFetchStyle;
|
import org.hibernate.loader.BatchFetchStyle;
|
||||||
import org.hibernate.proxy.EntityNotFoundDelegate;
|
import org.hibernate.proxy.EntityNotFoundDelegate;
|
||||||
|
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
||||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||||
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
||||||
|
|
||||||
|
@ -127,7 +129,13 @@ public interface SessionFactoryOptionsState {
|
||||||
|
|
||||||
public Integer getJdbcFetchSize();
|
public Integer getJdbcFetchSize();
|
||||||
|
|
||||||
public ConnectionReleaseMode getConnectionReleaseMode();
|
PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #getPhysicalConnectionHandlingMode()} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
ConnectionReleaseMode getConnectionReleaseMode();
|
||||||
|
|
||||||
public boolean isCommentsEnabled();
|
public boolean isCommentsEnabled();
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.hibernate.dialect.function.SQLFunction;
|
||||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||||
import org.hibernate.loader.BatchFetchStyle;
|
import org.hibernate.loader.BatchFetchStyle;
|
||||||
import org.hibernate.proxy.EntityNotFoundDelegate;
|
import org.hibernate.proxy.EntityNotFoundDelegate;
|
||||||
|
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
||||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||||
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public abstract class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOptions {
|
public abstract class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOptions {
|
||||||
private final SessionFactoryOptions delegate;
|
private final SessionFactoryOptions delegate;
|
||||||
|
|
||||||
|
@ -272,6 +274,12 @@ public abstract class AbstractDelegatingSessionFactoryOptions implements Session
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
public PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode() {
|
||||||
|
return delegate.getPhysicalConnectionHandlingMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public ConnectionReleaseMode getConnectionReleaseMode() {
|
public ConnectionReleaseMode getConnectionReleaseMode() {
|
||||||
return delegate.getConnectionReleaseMode();
|
return delegate.getConnectionReleaseMode();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.boot.spi;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.ConnectionAcquisitionMode;
|
||||||
import org.hibernate.ConnectionReleaseMode;
|
import org.hibernate.ConnectionReleaseMode;
|
||||||
import org.hibernate.CustomEntityDirtinessStrategy;
|
import org.hibernate.CustomEntityDirtinessStrategy;
|
||||||
import org.hibernate.EntityMode;
|
import org.hibernate.EntityMode;
|
||||||
|
@ -26,6 +27,7 @@ import org.hibernate.dialect.function.SQLFunction;
|
||||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||||
import org.hibernate.loader.BatchFetchStyle;
|
import org.hibernate.loader.BatchFetchStyle;
|
||||||
import org.hibernate.proxy.EntityNotFoundDelegate;
|
import org.hibernate.proxy.EntityNotFoundDelegate;
|
||||||
|
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
||||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||||
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
||||||
|
|
||||||
|
@ -151,7 +153,13 @@ public interface SessionFactoryOptions {
|
||||||
|
|
||||||
public Integer getJdbcFetchSize();
|
public Integer getJdbcFetchSize();
|
||||||
|
|
||||||
public ConnectionReleaseMode getConnectionReleaseMode();
|
PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #getPhysicalConnectionHandlingMode()} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
ConnectionReleaseMode getConnectionReleaseMode();
|
||||||
|
|
||||||
public boolean isCommentsEnabled();
|
public boolean isCommentsEnabled();
|
||||||
|
|
||||||
|
|
|
@ -513,7 +513,20 @@ public interface AvailableSettings {
|
||||||
String FLUSH_BEFORE_COMPLETION = "hibernate.transaction.flush_before_completion";
|
String FLUSH_BEFORE_COMPLETION = "hibernate.transaction.flush_before_completion";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies how Hibernate should release JDBC connections.
|
* Specifies how Hibernate should acquire JDBC connections. Should generally only configure
|
||||||
|
* this or {@link #RELEASE_CONNECTIONS}, not both
|
||||||
|
*
|
||||||
|
* @see org.hibernate.ConnectionAcquisitionMode
|
||||||
|
*
|
||||||
|
* @since 5.1
|
||||||
|
*/
|
||||||
|
String ACQUIRE_CONNECTIONS = "hibernate.connection.acquisition_mode";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies how Hibernate should release JDBC connections. Should generally only configure
|
||||||
|
* this or {@link #ACQUIRE_CONNECTIONS}, not both
|
||||||
|
*
|
||||||
|
* @see org.hibernate.ConnectionReleaseMode
|
||||||
*/
|
*/
|
||||||
String RELEASE_CONNECTIONS = "hibernate.connection.release_mode";
|
String RELEASE_CONNECTIONS = "hibernate.connection.release_mode";
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ import org.hibernate.procedure.internal.ProcedureCallImpl;
|
||||||
import org.hibernate.resource.jdbc.spi.JdbcObserver;
|
import org.hibernate.resource.jdbc.spi.JdbcObserver;
|
||||||
import org.hibernate.resource.jdbc.spi.JdbcSessionContext;
|
import org.hibernate.resource.jdbc.spi.JdbcSessionContext;
|
||||||
import org.hibernate.resource.jdbc.spi.JdbcSessionOwner;
|
import org.hibernate.resource.jdbc.spi.JdbcSessionOwner;
|
||||||
|
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
||||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||||
import org.hibernate.resource.transaction.TransactionCoordinatorBuilder;
|
import org.hibernate.resource.transaction.TransactionCoordinatorBuilder;
|
||||||
import org.hibernate.resource.transaction.TransactionCoordinatorBuilder.TransactionCoordinatorOptions;
|
import org.hibernate.resource.transaction.TransactionCoordinatorBuilder.TransactionCoordinatorOptions;
|
||||||
|
@ -456,12 +457,15 @@ public abstract class AbstractSessionImpl
|
||||||
public class JdbcSessionContextImpl implements JdbcSessionContext {
|
public class JdbcSessionContextImpl implements JdbcSessionContext {
|
||||||
private final SessionFactoryImpl sessionFactory;
|
private final SessionFactoryImpl sessionFactory;
|
||||||
private final StatementInspector inspector;
|
private final StatementInspector inspector;
|
||||||
|
private final PhysicalConnectionHandlingMode connectionHandlingMode;
|
||||||
|
|
||||||
private final transient ServiceRegistry serviceRegistry;
|
private final transient ServiceRegistry serviceRegistry;
|
||||||
private final transient JdbcObserver jdbcObserver;
|
private final transient JdbcObserver jdbcObserver;
|
||||||
|
|
||||||
public JdbcSessionContextImpl(SessionFactoryImpl sessionFactory, StatementInspector inspector) {
|
public JdbcSessionContextImpl(SessionFactoryImpl sessionFactory, StatementInspector inspector) {
|
||||||
this.sessionFactory = sessionFactory;
|
this.sessionFactory = sessionFactory;
|
||||||
this.inspector = inspector;
|
this.inspector = inspector;
|
||||||
|
this.connectionHandlingMode = settings().getPhysicalConnectionHandlingMode();
|
||||||
this.serviceRegistry = sessionFactory.getServiceRegistry();
|
this.serviceRegistry = sessionFactory.getServiceRegistry();
|
||||||
this.jdbcObserver = new JdbcObserverImpl();
|
this.jdbcObserver = new JdbcObserverImpl();
|
||||||
|
|
||||||
|
@ -485,14 +489,19 @@ public abstract class AbstractSessionImpl
|
||||||
return settings().getJdbcFetchSize();
|
return settings().getJdbcFetchSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode() {
|
||||||
|
return connectionHandlingMode;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConnectionReleaseMode getConnectionReleaseMode() {
|
public ConnectionReleaseMode getConnectionReleaseMode() {
|
||||||
return settings().getConnectionReleaseMode();
|
return connectionHandlingMode.getReleaseMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConnectionAcquisitionMode getConnectionAcquisitionMode() {
|
public ConnectionAcquisitionMode getConnectionAcquisitionMode() {
|
||||||
return ConnectionAcquisitionMode.DEFAULT;
|
return connectionHandlingMode.getAcquisitionMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -12,8 +12,6 @@ import java.io.ObjectOutputStream;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
import org.hibernate.ConnectionAcquisitionMode;
|
import org.hibernate.ConnectionAcquisitionMode;
|
||||||
import org.hibernate.ConnectionReleaseMode;
|
import org.hibernate.ConnectionReleaseMode;
|
||||||
import org.hibernate.ResourceClosedException;
|
import org.hibernate.ResourceClosedException;
|
||||||
|
@ -24,6 +22,9 @@ import org.hibernate.resource.jdbc.ResourceRegistry;
|
||||||
import org.hibernate.resource.jdbc.spi.JdbcObserver;
|
import org.hibernate.resource.jdbc.spi.JdbcObserver;
|
||||||
import org.hibernate.resource.jdbc.spi.JdbcSessionContext;
|
import org.hibernate.resource.jdbc.spi.JdbcSessionContext;
|
||||||
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
|
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
|
||||||
|
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a LogicalConnection where we manage obtaining and releasing the Connection as needed.
|
* Represents a LogicalConnection where we manage obtaining and releasing the Connection as needed.
|
||||||
|
@ -36,7 +37,8 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple
|
||||||
private final transient JdbcConnectionAccess jdbcConnectionAccess;
|
private final transient JdbcConnectionAccess jdbcConnectionAccess;
|
||||||
private final transient JdbcObserver observer;
|
private final transient JdbcObserver observer;
|
||||||
private final transient SqlExceptionHelper sqlExceptionHelper;
|
private final transient SqlExceptionHelper sqlExceptionHelper;
|
||||||
private final transient ConnectionReleaseMode connectionReleaseMode;
|
|
||||||
|
private final transient PhysicalConnectionHandlingMode physicalConnectionHandlingMode;
|
||||||
|
|
||||||
private transient Connection physicalConnection;
|
private transient Connection physicalConnection;
|
||||||
private boolean closed;
|
private boolean closed;
|
||||||
|
@ -57,15 +59,10 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple
|
||||||
this.sqlExceptionHelper = jdbcSessionContext.getServiceRegistry()
|
this.sqlExceptionHelper = jdbcSessionContext.getServiceRegistry()
|
||||||
.getService( JdbcServices.class )
|
.getService( JdbcServices.class )
|
||||||
.getSqlExceptionHelper();
|
.getSqlExceptionHelper();
|
||||||
this.connectionReleaseMode = jdbcSessionContext.getConnectionReleaseMode();
|
this.physicalConnectionHandlingMode = jdbcSessionContext.getPhysicalConnectionHandlingMode();
|
||||||
this.resourceRegistry = resourceRegistry;
|
this.resourceRegistry = resourceRegistry;
|
||||||
|
|
||||||
if ( jdbcSessionContext.getConnectionAcquisitionMode() == ConnectionAcquisitionMode.IMMEDIATELY ) {
|
if ( physicalConnectionHandlingMode.getAcquisitionMode() == ConnectionAcquisitionMode.IMMEDIATELY ) {
|
||||||
if ( jdbcSessionContext.getConnectionReleaseMode() != ConnectionReleaseMode.ON_CLOSE ) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Illegal combination of ConnectionAcquisitionMode#IMMEDIATELY with !ConnectionReleaseMode.ON_CLOSE"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
acquireConnectionIfNeeded();
|
acquireConnectionIfNeeded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +113,7 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple
|
||||||
public void afterStatement() {
|
public void afterStatement() {
|
||||||
super.afterStatement();
|
super.afterStatement();
|
||||||
|
|
||||||
if ( connectionReleaseMode == ConnectionReleaseMode.AFTER_STATEMENT ) {
|
if ( physicalConnectionHandlingMode.getReleaseMode() == ConnectionReleaseMode.AFTER_STATEMENT ) {
|
||||||
if ( getResourceRegistry().hasRegisteredResources() ) {
|
if ( getResourceRegistry().hasRegisteredResources() ) {
|
||||||
log.debug( "Skipping aggressive release of JDBC Connection after-statement due to held resources" );
|
log.debug( "Skipping aggressive release of JDBC Connection after-statement due to held resources" );
|
||||||
}
|
}
|
||||||
|
@ -131,7 +128,7 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple
|
||||||
public void afterTransaction() {
|
public void afterTransaction() {
|
||||||
super.afterTransaction();
|
super.afterTransaction();
|
||||||
|
|
||||||
if ( connectionReleaseMode != ConnectionReleaseMode.ON_CLOSE ) {
|
if ( physicalConnectionHandlingMode.getReleaseMode() != ConnectionReleaseMode.ON_CLOSE ) {
|
||||||
// NOTE : we check for !ON_CLOSE here (rather than AFTER_TRANSACTION) to also catch AFTER_STATEMENT cases
|
// NOTE : we check for !ON_CLOSE here (rather than AFTER_TRANSACTION) to also catch AFTER_STATEMENT cases
|
||||||
// that were circumvented due to held resources
|
// that were circumvented due to held resources
|
||||||
log.debug( "Initiating JDBC connection release from afterTransaction" );
|
log.debug( "Initiating JDBC connection release from afterTransaction" );
|
||||||
|
|
|
@ -17,23 +17,34 @@ import org.hibernate.service.ServiceRegistry;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface JdbcSessionContext {
|
public interface JdbcSessionContext {
|
||||||
public boolean isScrollableResultSetsEnabled();
|
boolean isScrollableResultSetsEnabled();
|
||||||
public boolean isGetGeneratedKeysEnabled();
|
boolean isGetGeneratedKeysEnabled();
|
||||||
public int getFetchSize();
|
int getFetchSize();
|
||||||
|
|
||||||
public ConnectionReleaseMode getConnectionReleaseMode();
|
PhysicalConnectionHandlingMode getPhysicalConnectionHandlingMode();
|
||||||
public ConnectionAcquisitionMode getConnectionAcquisitionMode();
|
|
||||||
|
|
||||||
public StatementInspector getStatementInspector();
|
/**
|
||||||
|
* @deprecated Use {@link #getPhysicalConnectionHandlingMode} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
ConnectionReleaseMode getConnectionReleaseMode();
|
||||||
|
|
||||||
public JdbcObserver getObserver();
|
/**
|
||||||
|
* @deprecated Use {@link #getPhysicalConnectionHandlingMode} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
ConnectionAcquisitionMode getConnectionAcquisitionMode();
|
||||||
|
|
||||||
|
StatementInspector getStatementInspector();
|
||||||
|
|
||||||
|
JdbcObserver getObserver();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the session factory for this environment.
|
* Retrieve the session factory for this environment.
|
||||||
*
|
*
|
||||||
* @return The session factory
|
* @return The session factory
|
||||||
*/
|
*/
|
||||||
public SessionFactoryImplementor getSessionFactory();
|
SessionFactoryImplementor getSessionFactory();
|
||||||
|
|
||||||
public ServiceRegistry getServiceRegistry();
|
ServiceRegistry getServiceRegistry();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* 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.resource.jdbc.spi;
|
||||||
|
|
||||||
|
import org.hibernate.ConnectionAcquisitionMode;
|
||||||
|
import org.hibernate.ConnectionReleaseMode;
|
||||||
|
|
||||||
|
import static org.hibernate.ConnectionAcquisitionMode.AS_NEEDED;
|
||||||
|
import static org.hibernate.ConnectionAcquisitionMode.IMMEDIATELY;
|
||||||
|
import static org.hibernate.ConnectionReleaseMode.AFTER_STATEMENT;
|
||||||
|
import static org.hibernate.ConnectionReleaseMode.AFTER_TRANSACTION;
|
||||||
|
import static org.hibernate.ConnectionReleaseMode.ON_CLOSE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents valid combinations of ConnectionAcquisitionMode and ConnectionReleaseMode
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public enum PhysicalConnectionHandlingMode {
|
||||||
|
/**
|
||||||
|
* The Connection will be acquired as soon as the Session is opened and
|
||||||
|
* held until the Session is closed. This is the only valid combination
|
||||||
|
* including immediate acquisition of the Connection
|
||||||
|
*/
|
||||||
|
IMMEDIATE_ACQUISITION_AND_HOLD( IMMEDIATELY, ON_CLOSE ),
|
||||||
|
/**
|
||||||
|
* The Connection will be acquired as soon as it is needed and then held
|
||||||
|
* until the Session is closed. This is the original Hibernate behavior.
|
||||||
|
*/
|
||||||
|
DELAYED_ACQUISITION_AND_HOLD( AS_NEEDED, ON_CLOSE ),
|
||||||
|
/**
|
||||||
|
* The Connection will be acquired as soon as it is needed; it will be released
|
||||||
|
* after each statement is executed.
|
||||||
|
*/
|
||||||
|
DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT( AS_NEEDED, AFTER_STATEMENT ),
|
||||||
|
/**
|
||||||
|
* The Connection will be acquired as soon as it is needed; it will be released
|
||||||
|
* after each transaction is completed.
|
||||||
|
*/
|
||||||
|
DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION( AS_NEEDED, AFTER_TRANSACTION )
|
||||||
|
;
|
||||||
|
|
||||||
|
private final ConnectionAcquisitionMode acquisitionMode;
|
||||||
|
private final ConnectionReleaseMode releaseMode;
|
||||||
|
|
||||||
|
PhysicalConnectionHandlingMode(
|
||||||
|
ConnectionAcquisitionMode acquisitionMode,
|
||||||
|
ConnectionReleaseMode releaseMode) {
|
||||||
|
this.acquisitionMode = acquisitionMode;
|
||||||
|
this.releaseMode = releaseMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionAcquisitionMode getAcquisitionMode() {
|
||||||
|
return acquisitionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionReleaseMode getReleaseMode() {
|
||||||
|
return releaseMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PhysicalConnectionHandlingMode interpret(
|
||||||
|
ConnectionAcquisitionMode acquisitionMode,
|
||||||
|
ConnectionReleaseMode releaseMode) {
|
||||||
|
if ( acquisitionMode == IMMEDIATELY ) {
|
||||||
|
if ( releaseMode != null && releaseMode != ON_CLOSE ) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Only ConnectionReleaseMode.ON_CLOSE can be used in combination with " +
|
||||||
|
"ConnectionAcquisitionMode.IMMEDIATELY; but ConnectionReleaseMode." +
|
||||||
|
releaseMode.name() + " was specified."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return IMMEDIATE_ACQUISITION_AND_HOLD;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch ( releaseMode ) {
|
||||||
|
case AFTER_STATEMENT: {
|
||||||
|
return DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT;
|
||||||
|
}
|
||||||
|
case AFTER_TRANSACTION: {
|
||||||
|
return DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return DELAYED_ACQUISITION_AND_HOLD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ public interface TransactionCoordinatorBuilder extends Service {
|
||||||
/**
|
/**
|
||||||
* Access to options to are specific to each TransactionCoordinator instance
|
* Access to options to are specific to each TransactionCoordinator instance
|
||||||
*/
|
*/
|
||||||
public static interface TransactionCoordinatorOptions {
|
static interface TransactionCoordinatorOptions {
|
||||||
/**
|
/**
|
||||||
* Indicates whether an active transaction should be automatically joined. Only relevant
|
* Indicates whether an active transaction should be automatically joined. Only relevant
|
||||||
* for JTA-based TransactionCoordinator instances.
|
* for JTA-based TransactionCoordinator instances.
|
||||||
|
@ -29,14 +29,14 @@ public interface TransactionCoordinatorBuilder extends Service {
|
||||||
* @return {@code true} indicates the active transaction should be auto joined; {@code false}
|
* @return {@code true} indicates the active transaction should be auto joined; {@code false}
|
||||||
* indicates it should not (until {@link TransactionCoordinator#explicitJoin} is called).
|
* indicates it should not (until {@link TransactionCoordinator#explicitJoin} is called).
|
||||||
*/
|
*/
|
||||||
public boolean shouldAutoJoinTransaction();
|
boolean shouldAutoJoinTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransactionCoordinator buildTransactionCoordinator(TransactionCoordinatorOwner owner, TransactionCoordinatorOptions options);
|
TransactionCoordinator buildTransactionCoordinator(TransactionCoordinatorOwner owner, TransactionCoordinatorOptions options);
|
||||||
|
|
||||||
public boolean isJta();
|
boolean isJta();
|
||||||
|
|
||||||
public ConnectionReleaseMode getDefaultConnectionReleaseMode();
|
ConnectionReleaseMode getDefaultConnectionReleaseMode();
|
||||||
|
|
||||||
public ConnectionAcquisitionMode getDefaultConnectionAcquisitionMode();
|
ConnectionAcquisitionMode getDefaultConnectionAcquisitionMode();
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,6 @@ public class JdbcResourceLocalTransactionCoordinatorBuilderImpl implements Trans
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConnectionAcquisitionMode getDefaultConnectionAcquisitionMode() {
|
public ConnectionAcquisitionMode getDefaultConnectionAcquisitionMode() {
|
||||||
return ConnectionAcquisitionMode.DEFAULT;
|
return ConnectionAcquisitionMode.AS_NEEDED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,6 @@ public class JtaTransactionCoordinatorBuilderImpl implements TransactionCoordina
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConnectionAcquisitionMode getDefaultConnectionAcquisitionMode() {
|
public ConnectionAcquisitionMode getDefaultConnectionAcquisitionMode() {
|
||||||
return ConnectionAcquisitionMode.DEFAULT;
|
return ConnectionAcquisitionMode.AS_NEEDED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue