Skip to content

Commit 0554b0a

Browse files
committed
HHH-19708 allow regular ConnectionProvider to handle schema/read-only
as requested by @beikov
1 parent 09f3021 commit 0554b0a

File tree

5 files changed

+107
-10
lines changed

5 files changed

+107
-10
lines changed

hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/ConnectionProvider.java

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.sql.Connection;
88
import java.sql.SQLException;
99

10+
import org.hibernate.Incubating;
1011
import org.hibernate.dialect.Dialect;
1112
import org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl;
1213
import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData;
@@ -40,20 +41,62 @@ public interface ConnectionProvider extends Service, Wrapped {
4041
* @return The obtained JDBC connection
4142
*
4243
* @throws SQLException Indicates a problem opening a connection
43-
* @throws org.hibernate.HibernateException Indicates a problem otherwise obtaining a connection.
44+
* @throws org.hibernate.HibernateException Indicates a problem obtaining a connection.
4445
*/
4546
Connection getConnection() throws SQLException;
4647

48+
/**
49+
* Obtains a connection to a read-only replica for use according to the underlying
50+
* strategy of this provider.
51+
*
52+
* @return The obtained JDBC connection
53+
*
54+
* @throws SQLException Indicates a problem opening a connection
55+
* @throws org.hibernate.HibernateException Indicates a problem obtaining a connection.
56+
*
57+
* @implNote This default implementation simply calls {@link #getConnection()},
58+
* which returns a connection to a writable replica. If this operation is overridden
59+
* to return a connection to a distinct read-only replica, the matching operation
60+
* {@link #closeReadOnlyConnection(Connection)} must also be overridden.
61+
*
62+
* @since 7.2
63+
*/
64+
@Incubating
65+
default Connection getReadOnlyConnection() throws SQLException {
66+
return getConnection();
67+
}
68+
4769
/**
4870
* Release a connection from Hibernate use.
4971
*
5072
* @param connection The JDBC connection to release
5173
*
5274
* @throws SQLException Indicates a problem closing the connection
53-
* @throws org.hibernate.HibernateException Indicates a problem otherwise releasing a connection.
75+
* @throws org.hibernate.HibernateException Indicates a problem releasing a connection.
5476
*/
5577
void closeConnection(Connection connection) throws SQLException;
5678

79+
/**
80+
* Release a connection to a read-only replica from Hibernate use.
81+
*
82+
* @param connection The JDBC connection to release
83+
*
84+
* @throws SQLException Indicates a problem closing the connection
85+
* @throws org.hibernate.HibernateException Indicates a problem otherwise releasing a connection.
86+
*
87+
* @implNote This default implementation simply calls
88+
* {@link #closeConnection(Connection)}. If
89+
* {@link #getReadOnlyConnection()} is overridden to return a
90+
* connection to a distinct read-only replica, this operation must also
91+
* be overridden.
92+
*
93+
* @since 7.2
94+
*/
95+
@Incubating
96+
default void closeReadOnlyConnection(Connection connection) throws SQLException {
97+
closeConnection( connection );
98+
}
99+
57100
/**
58101
* Does this connection provider support aggressive release of JDBC connections and later
59102
* re-acquisition of those connections if needed?
@@ -72,6 +115,34 @@ public interface ConnectionProvider extends Service, Wrapped {
72115
*/
73116
boolean supportsAggressiveRelease();
74117

118+
/**
119+
* Does this connection provider correctly set the
120+
* {@linkplain java.sql.Connection#setSchema schema}
121+
* of the returned JDBC connections?
122+
* @return {@code true} if the connection provider handles this;
123+
* {@code false} if the client should set the schema
124+
*
125+
* @implNote If necessary, a {@code ConnectionProvider} may
126+
* call {@link org.hibernate.context.spi.MultiTenancy#getTenantSchemaMapper}
127+
* to obtain the {@link org.hibernate.context.spi.TenantSchemaMapper}.
128+
*/
129+
@Incubating
130+
default boolean handlesConnectionSchema() {
131+
return false;
132+
}
133+
134+
/**
135+
* Does this connection provider correctly set the
136+
* {@linkplain java.sql.Connection#setReadOnly read-only mode}
137+
* of the returned JDBC connections?
138+
* @return {@code true} if the connection provider handles this;
139+
* {@code false} if the client should set the read-only mode
140+
*/
141+
@Incubating
142+
default boolean handlesConnectionReadOnly() {
143+
return false;
144+
}
145+
75146
/**
76147
* @return an informative instance of {@link DatabaseConnectionInfo} for logging.
77148
*

hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/spi/MultiTenantConnectionProvider.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ public interface MultiTenantConnectionProvider<T> extends Service, Wrapped {
7777
* @throws org.hibernate.HibernateException Indicates a problem obtaining a connection
7878
*
7979
* @implNote This default implementation simply calls {@link #getConnection(Object)},
80-
* which returns a connection a writable replica. If this operation is overridden to
81-
* return a connection to a distinct read-only replica, the matching operation
80+
* which returns a connection to a writable replica. If this operation is overridden
81+
* to return a connection to a distinct read-only replica, the matching operation
8282
* {@link #releaseReadOnlyConnection(Object, Connection)} must also be overridden.
8383
*
8484
* @since 7.2
@@ -147,7 +147,12 @@ default void releaseReadOnlyConnection(T tenantIdentifier, Connection connection
147147
* of the returned JDBC connections?
148148
* @return {@code true} if the connection provider handles this;
149149
* {@code false} if the client should set the schema
150+
*
151+
* @implNote If necessary, a {@code ConnectionProvider} may
152+
* call {@link org.hibernate.context.spi.MultiTenancy#getTenantSchemaMapper}
153+
* to obtain the {@link org.hibernate.context.spi.TenantSchemaMapper}.
150154
*/
155+
@Incubating
151156
default boolean handlesConnectionSchema() {
152157
return false;
153158
}
@@ -159,6 +164,7 @@ default boolean handlesConnectionSchema() {
159164
* @return {@code true} if the connection provider handles this;
160165
* {@code false} if the client should set the read-only mode
161166
*/
167+
@Incubating
162168
default boolean handlesConnectionReadOnly() {
163169
return false;
164170
}

hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,7 @@ public JdbcConnectionAccess getJdbcConnectionAccess() {
687687
if ( !factoryOptions.isMultiTenancyEnabled() ) {
688688
// we might still be using schema-based multitenancy
689689
jdbcConnectionAccess = new NonContextualJdbcConnectionAccess(
690+
readOnly,
690691
sessionEventsManager,
691692
factory.connectionProvider,
692693
this
@@ -707,13 +708,11 @@ public JdbcConnectionAccess getJdbcConnectionAccess() {
707708
}
708709

709710
private boolean manageReadOnly() {
710-
return factory.multiTenantConnectionProvider == null
711-
|| !factory.multiTenantConnectionProvider.handlesConnectionReadOnly();
711+
return !factory.connectionProviderHandlesConnectionReadOnly();
712712
}
713713

714714
private boolean manageSchema() {
715-
return factory.multiTenantConnectionProvider == null
716-
|| !factory.multiTenantConnectionProvider.handlesConnectionSchema();
715+
return !factory.connectionProviderHandlesConnectionSchema();
717716
}
718717

719718
private boolean useSchemaBasedMultiTenancy() {

hibernate-core/src/main/java/org/hibernate/internal/NonContextualJdbcConnectionAccess.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,19 @@
1818
* @author Steve Ebersole
1919
*/
2020
public class NonContextualJdbcConnectionAccess implements JdbcConnectionAccess, Serializable {
21+
private final boolean readOnly;
2122
private final SessionEventListener listener;
2223
private final ConnectionProvider connectionProvider;
2324
private final SharedSessionContractImplementor session;
2425

2526
public NonContextualJdbcConnectionAccess(
27+
boolean readOnly,
2628
SessionEventListener listener,
2729
ConnectionProvider connectionProvider,
2830
SharedSessionContractImplementor session) {
2931
Objects.requireNonNull( listener );
3032
Objects.requireNonNull( connectionProvider );
33+
this.readOnly = readOnly;
3134
this.listener = listener;
3235
this.connectionProvider = connectionProvider;
3336
this.session = session;
@@ -39,7 +42,9 @@ public Connection obtainConnection() throws SQLException {
3942
final var connectionAcquisitionEvent = eventMonitor.beginJdbcConnectionAcquisitionEvent();
4043
try {
4144
listener.jdbcConnectionAcquisitionStart();
42-
return connectionProvider.getConnection();
45+
return readOnly
46+
? connectionProvider.getReadOnlyConnection()
47+
: connectionProvider.getConnection();
4348
}
4449
finally {
4550
eventMonitor.completeJdbcConnectionAcquisitionEvent( connectionAcquisitionEvent, session, null );
@@ -53,7 +58,12 @@ public void releaseConnection(Connection connection) throws SQLException {
5358
final var connectionReleaseEvent = eventMonitor.beginJdbcConnectionReleaseEvent();
5459
try {
5560
listener.jdbcConnectionReleaseStart();
56-
connectionProvider.closeConnection( connection );
61+
if ( readOnly ) {
62+
connectionProvider.closeReadOnlyConnection( connection );
63+
}
64+
else {
65+
connectionProvider.closeConnection( connection );
66+
}
5767
}
5868
finally {
5969
eventMonitor.completeJdbcConnectionReleaseEvent( connectionReleaseEvent, session, null );

hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,6 +1577,17 @@ public JavaType<Object> getTenantIdentifierJavaType() {
15771577
return tenantIdentifierJavaType;
15781578
}
15791579

1580+
boolean connectionProviderHandlesConnectionReadOnly() {
1581+
return multiTenantConnectionProvider != null
1582+
? multiTenantConnectionProvider.handlesConnectionReadOnly()
1583+
: connectionProvider.handlesConnectionReadOnly();
1584+
}
1585+
1586+
boolean connectionProviderHandlesConnectionSchema() {
1587+
return multiTenantConnectionProvider != null
1588+
? multiTenantConnectionProvider.handlesConnectionSchema()
1589+
: connectionProvider.handlesConnectionSchema();
1590+
}
15801591

15811592
// Serialization handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15821593

0 commit comments

Comments
 (0)