HHH-18220 Detect if Application Continuity is enabled for Oracle dialect

This commit is contained in:
LLEFEVRE 2024-06-03 12:08:00 +02:00 committed by Christian Beikov
parent 58e814965e
commit 7c711751dd
3 changed files with 95 additions and 11 deletions

View File

@ -12,6 +12,7 @@ package org.hibernate.cfg;
* its underlying JDBC {@link java.sql.Connection}.
*
* @author Marco Belladelli
* @author Loïc Lefèvre
*/
public interface DialectSpecificSettings {
/**
@ -28,6 +29,15 @@ public interface DialectSpecificSettings {
*/
public static final String ORACLE_EXTENDED_STRING_SIZE = "hibernate.dialect.oracle.extended_string_size";
/**
* Specifies whether this database is accessed using a database service protected by Application Continuity.
*
* @settingDefault {@code false}
*
* @see <a href="https://docs.oracle.com/en/database/oracle/oracle-database/23/jjdbc/application-continuity.html">Application Continuity for Java</a>
*/
public static final String ORACLE_APPLICATION_CONTINUITY = "hibernate.dialect.oracle.application_continuity";
/**
* Specifies whether this database's {@code ansinull} setting is enabled.
*

View File

@ -187,6 +187,9 @@ public class OracleDialect extends Dialect {
// Is MAX_STRING_SIZE set to EXTENDED?
protected final boolean extended;
// Is the database accessed using a database service protected by Application Continuity.
protected final boolean applicationContinuity;
protected final int driverMajorVersion;
protected final int driverMinorVersion;
@ -200,6 +203,7 @@ public class OracleDialect extends Dialect {
super(version);
autonomous = false;
extended = false;
applicationContinuity = false;
driverMajorVersion = 19;
driverMinorVersion = 0;
}
@ -212,6 +216,7 @@ public class OracleDialect extends Dialect {
super( info );
autonomous = serverConfiguration.isAutonomous();
extended = serverConfiguration.isExtended();
applicationContinuity = serverConfiguration.isApplicationContinuity();
this.driverMinorVersion = serverConfiguration.getDriverMinorVersion();
this.driverMajorVersion = serverConfiguration.getDriverMajorVersion();
}
@ -258,6 +263,10 @@ public class OracleDialect extends Dialect {
return extended;
}
public boolean isApplicationContinuity() {
return applicationContinuity;
}
@Override
protected DatabaseVersion getMinimumSupportedVersion() {
return MINIMUM_VERSION;

View File

@ -6,15 +6,20 @@
*/
package org.hibernate.dialect;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import oracle.jdbc.replay.ReplayStatistics;
import oracle.jdbc.replay.ReplayableConnection;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.internal.util.config.ConfigurationHelper;
import static oracle.jdbc.replay.ReplayableConnection.StatisticsReportType.FOR_CURRENT_CONNECTION;
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_APPLICATION_CONTINUITY;
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_AUTONOMOUS_DATABASE;
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_EXTENDED_STRING_SIZE;
@ -22,10 +27,12 @@ import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_EXTENDED_STRING_S
* Utility class that extract some initial configuration from the database for {@link OracleDialect}.
*
* @author Marco Belladelli
* @author Loïc Lefèvre
*/
public class OracleServerConfiguration {
private final boolean autonomous;
private final boolean extended;
private final boolean applicationContinuity;
private final int driverMajorVersion;
private final int driverMinorVersion;
@ -37,6 +44,10 @@ public class OracleServerConfiguration {
return extended;
}
public boolean isApplicationContinuity() {
return applicationContinuity;
}
public int getDriverMajorVersion() {
return driverMajorVersion;
}
@ -46,7 +57,7 @@ public class OracleServerConfiguration {
}
public OracleServerConfiguration(boolean autonomous, boolean extended) {
this( autonomous, extended, 19, 0 );
this( autonomous, extended, false, 19, 0 );
}
public OracleServerConfiguration(
@ -54,8 +65,18 @@ public class OracleServerConfiguration {
boolean extended,
int driverMajorVersion,
int driverMinorVersion) {
this(autonomous, extended, false, driverMajorVersion, driverMinorVersion);
}
public OracleServerConfiguration(
boolean autonomous,
boolean extended,
boolean applicationContinuity,
int driverMajorVersion,
int driverMinorVersion) {
this.autonomous = autonomous;
this.extended = extended;
this.applicationContinuity = applicationContinuity;
this.driverMajorVersion = driverMajorVersion;
this.driverMinorVersion = driverMinorVersion;
}
@ -63,6 +84,7 @@ public class OracleServerConfiguration {
public static OracleServerConfiguration fromDialectResolutionInfo(DialectResolutionInfo info) {
Boolean extended = null;
Boolean autonomous = null;
Boolean applicationContinuity = null;
Integer majorVersion = null;
Integer minorVersion = null;
final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
@ -70,15 +92,51 @@ public class OracleServerConfiguration {
majorVersion = databaseMetaData.getDriverMajorVersion();
minorVersion = databaseMetaData.getDriverMinorVersion();
try (final Statement statement = databaseMetaData.getConnection().createStatement()) {
final ResultSet rs = statement.executeQuery(
"select cast('string' as varchar2(32000)), " +
"sys_context('USERENV','CLOUD_SERVICE') from dual"
);
if ( rs.next() ) {
// succeeded, so MAX_STRING_SIZE == EXTENDED
extended = true;
autonomous = isAutonomous( rs.getString( 2 ) );
try {
final Connection c = databaseMetaData.getConnection();
// Use Oracle JDBC replay statistics information to determine if this
// connection is protected by Application Continuity
try {
final ReplayableConnection re = (ReplayableConnection) c;
ReplayStatistics stats = re.getReplayStatistics(FOR_CURRENT_CONNECTION);
final long totalRequests = stats.getTotalRequests();
final long protectedCalls = stats.getTotalProtectedCalls();
try (final Statement s = c.createStatement()) {
try (final ResultSet r = s.executeQuery("select 1 from dual")) {
r.next();
}
}
stats = re.getReplayStatistics(FOR_CURRENT_CONNECTION);
// Application continuity is enabled on this database service if the number of
// total requests and the number of protected calls for this connection have
// both increased.
applicationContinuity = stats.getTotalRequests() > totalRequests && stats.getTotalProtectedCalls() > protectedCalls;
}
catch(Exception e) {
// A ClassCastException or a NullPointerException are expected here in the case
// the Connection Factory is not the right one (not Replayable: ClassCastException)
// or if the database service has not been configured (server side) to enable
// application continuity (NullPointerException).
applicationContinuity = false;
}
// continue the checks...
try (final Statement statement = c.createStatement()) {
final ResultSet rs = statement.executeQuery(
"select cast('string' as varchar2(32000)), " +
"sys_context('USERENV','CLOUD_SERVICE') from dual"
);
if (rs.next()) {
// succeeded, so MAX_STRING_SIZE == EXTENDED
extended = true;
autonomous = isAutonomous(rs.getString(2));
}
}
}
catch (SQLException ex) {
@ -102,6 +160,13 @@ public class OracleServerConfiguration {
false
);
}
if ( applicationContinuity == null ) {
applicationContinuity = ConfigurationHelper.getBoolean(
ORACLE_APPLICATION_CONTINUITY,
info.getConfigurationValues(),
false
);
}
if ( majorVersion == null ) {
try {
java.sql.Driver driver = java.sql.DriverManager.getDriver( "jdbc:oracle:thin:" );
@ -114,7 +179,7 @@ public class OracleServerConfiguration {
}
}
return new OracleServerConfiguration( autonomous, extended, majorVersion, minorVersion );
return new OracleServerConfiguration( autonomous, extended, applicationContinuity, majorVersion, minorVersion );
}
private static boolean isAutonomous(String cloudServiceParam) {