Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT] Upgrade Vert.x SQL client to 5 #2038

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -39,11 +39,11 @@ Hibernate Reactive has been tested with:
- MS SQL Server 2022
- Oracle 23
- [Hibernate ORM][] 7.0.0.Beta4
- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 4.5.13
- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 4.5.13
- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 4.5.13
- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 4.5.13
- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 4.5.13
- [Vert.x Reactive PostgreSQL Client](https://vertx.io/docs/vertx-pg-client/java/) 5.0.0.CR4
- [Vert.x Reactive MySQL Client](https://vertx.io/docs/vertx-mysql-client/java/) 5.0.0.CR4
- [Vert.x Reactive Db2 Client](https://vertx.io/docs/vertx-db2-client/java/) 5.0.0.CR4
- [Vert.x Reactive MS SQL Server Client](https://vertx.io/docs/vertx-mssql-client/java/) 5.0.0.CR4
- [Vert.x Reactive Oracle Client](https://vertx.io/docs/vertx-oracle-client/java/) 5.0.0.CR4
- [Quarkus][Quarkus] via the Hibernate Reactive extension

[PostgreSQL]: https://www.postgresql.org
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ ext {
// Example:
// ./gradlew build -PvertxSqlClientVersion=4.0.0-SNAPSHOT
if ( !project.hasProperty( 'vertxSqlClientVersion' ) ) {
vertxSqlClientVersion = '4.5.13'
vertxSqlClientVersion = '5.0.0.CR4'
}

testcontainersVersion = '1.20.4'
Original file line number Diff line number Diff line change
@@ -89,7 +89,7 @@ Optionally, you might also add any of the following additional features:
| Hibernate Validator | `org.hibernate.validator:hibernate-validator` and `org.glassfish:jakarta.el`
| Compile-time checking for your HQL queries | `org.hibernate:query-validator`
| Second-level cache support via JCache and EHCache | `org.hibernate.orm:hibernate-jcache` along with `org.ehcache:ehcache`
| SCRAM authentication support for PostgreSQL | `com.ongres.scram:client:2.1`
| SCRAM authentication support for PostgreSQL | `com.ongres.scram:scram-client:3.1`
|===

You might also add the Hibernate {enhancer}[bytecode enhancer] to your
2 changes: 1 addition & 1 deletion examples/native-sql-example/build.gradle
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@ dependencies {
runtimeOnly "org.apache.logging.log4j:log4j-core:2.20.0"

// Allow authentication to PostgreSQL using SCRAM:
runtimeOnly 'com.ongres.scram:client:2.1'
runtimeOnly 'com.ongres.scram:scram-client:3.1'
}

// Optional: enable the bytecode enhancements
2 changes: 1 addition & 1 deletion examples/session-example/build.gradle
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ dependencies {
runtimeOnly "org.apache.logging.log4j:log4j-core:2.20.0"

// Allow authentication to PostgreSQL using SCRAM:
runtimeOnly 'com.ongres.scram:client:2.1'
runtimeOnly 'com.ongres.scram:scram-client:3.1'
}

// Optional: enable the bytecode enhancements
6 changes: 3 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -47,9 +47,9 @@ hibernateOrmVersion = 7.0.0.Beta4
#skipOrmVersionParsing = true

# Override default Vert.x Sql client version
#vertxSqlClientVersion = 4.5.13-SNAPSHOT
#vertxSqlClientVersion = 5.0.0.CR4-SNAPSHOT

# Override default Vert.x Web client and server versions. For integration tests, both default to vertxSqlClientVersion
#vertxWebVersion = 4.5.13
#vertxWebtClientVersion = 4.5.13
#vertxWebVersion = 5.0.0.CR4
#vertxWebtClientVersion = 5.0.0.CR4

2 changes: 1 addition & 1 deletion hibernate-reactive-core/build.gradle
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ dependencies {
testImplementation "io.vertx:vertx-micrometer-metrics:${vertxSqlClientVersion}"

// Optional dependency of vertx-pg-client, essential when connecting via SASL SCRAM
testImplementation 'com.ongres.scram:client:2.1'
testImplementation 'com.ongres.scram:scram-client:3.1'

// JUnit Jupiter
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.3'
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
import java.lang.invoke.MethodHandles;

import io.vertx.core.Vertx;
import io.vertx.core.impl.ContextInternal;
import io.vertx.core.internal.ContextInternal;

import org.hibernate.reactive.context.Context;
import org.hibernate.reactive.logging.impl.Log;
@@ -36,7 +36,7 @@ public void injectServices(ServiceRegistryImplementor serviceRegistry) {

@Override
public <T> void put(Key<T> key, T instance) {
final io.vertx.core.Context context = Vertx.currentContext();
final ContextInternal context = currentContext();
if ( context != null ) {
if ( trace ) LOG.tracef( "Putting key,value in context: [%1$s, %2$s]", key, instance );
context.putLocal( key, instance );
@@ -47,9 +47,13 @@ public <T> void put(Key<T> key, T instance) {
}
}

private static ContextInternal currentContext() {
return (ContextInternal) Vertx.currentContext();
}

@Override
public <T> T get(Key<T> key) {
final io.vertx.core.Context context = Vertx.currentContext();
final ContextInternal context = currentContext();
if ( context != null ) {
T local = context.getLocal( key );
if ( trace ) LOG.tracef( "Getting value %2$s from context for key %1$s", key, local );
@@ -63,7 +67,7 @@ public <T> T get(Key<T> key) {

@Override
public void remove(Key<?> key) {
final io.vertx.core.Context context = Vertx.currentContext();
final ContextInternal context = currentContext();
if ( context != null ) {
boolean removed = context.removeLocal( key );
if ( trace ) LOG.tracef( "Key %s removed from context: %s", key, removed );
@@ -75,14 +79,15 @@ public void remove(Key<?> key) {

@Override
public void execute(Runnable runnable) {
final io.vertx.core.Context currentContext = Vertx.currentContext();
final io.vertx.core.Context currentContext = currentContext();
if ( currentContext == null ) {
if ( trace ) LOG.tracef( "Not in a Vert.x context, checking the VertxInstance service" );
final io.vertx.core.Context newContext = vertxInstance.getVertx().getOrCreateContext();
// Ensure we don't run on the root context, which is globally scoped:
// that could lead to unintentionally share the same session with other streams.
ContextInternal newContextInternal = (ContextInternal) newContext;
final ContextInternal duplicate = newContextInternal.duplicate();

if ( trace ) LOG.tracef( "Using duplicated context from VertxInstance: %s", duplicate );
duplicate.runOnContext( x -> runnable.run() );
}
Original file line number Diff line number Diff line change
@@ -14,9 +14,9 @@

import io.vertx.core.Context;
import io.vertx.core.Vertx;
import io.vertx.core.net.impl.pool.CombinerExecutor;
import io.vertx.core.net.impl.pool.Executor;
import io.vertx.core.net.impl.pool.Task;
import io.vertx.core.internal.pool.CombinerExecutor;
import io.vertx.core.internal.pool.Executor;
import io.vertx.core.internal.pool.Task;

import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture;

@@ -44,7 +44,7 @@ public abstract class BlockingIdentifierGenerator implements ReactiveIdentifierG
//modification access.
//This replaces the synchronization blocks one would see in a similar
//service in Hibernate ORM, but using a non-blocking cooperative design.
private final CombinerExecutor executor = new CombinerExecutor( state );
private final CombinerExecutor<GeneratorState> executor = new CombinerExecutor<>( state );

/**
* Allocate a new block, by obtaining the next "hi" value from the database
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.concurrent.CompletionStage;
import java.util.function.Supplier;

import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
@@ -31,13 +32,13 @@

import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.net.NetClientOptions;
import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.PoolOptions;
import io.vertx.sqlclient.SqlConnectOptions;
import io.vertx.sqlclient.impl.Utils;
import io.vertx.sqlclient.spi.Driver;

import static java.util.Collections.singletonList;

/**
* A pool of reactive connections backed by a Vert.x {@link Pool}.
* The {@code Pool} itself is backed by an instance of {@link Vertx}
@@ -189,7 +190,7 @@ protected Pool createPool(URI uri) {
*
* @return the new {@link Pool}
*/
protected Pool createPool(URI uri, SqlConnectOptions connectOptions, PoolOptions poolOptions, Vertx vertx) {
protected <T extends SqlConnectOptions> Pool createPool(URI uri, T connectOptions, PoolOptions poolOptions, Vertx vertx) {
try {
// First try to load the Pool using the standard ServiceLoader pattern
// This only works if exactly 1 Driver is on the classpath.
@@ -198,8 +199,9 @@ protected Pool createPool(URI uri, SqlConnectOptions connectOptions, PoolOptions
catch (ServiceConfigurationError e) {
// Backup option if multiple drivers are on the classpath.
// We will be able to remove this once Vertx 3.9.2 is available
final Driver driver = findDriver( uri, e );
return driver.createPool( vertx, singletonList( connectOptions ), poolOptions );
final Driver<SqlConnectOptions> driver = findDriver( uri, e );
Supplier<Future<SqlConnectOptions>> database = Utils.singletonSupplier( driver.downcast( connectOptions ) );
return driver.createPool( vertx, database, poolOptions, new NetClientOptions(), null );
}
}

@@ -222,15 +224,14 @@ protected URI jdbcUrl(Map<?,?> configurationValues) {
* so we need to disambiguate according to the scheme specified
* in the given {@link URI}.
*
* @param uri the JDBC URL or database URI
* @param uri the JDBC URL or database URI
* @param originalError the error that was thrown
*
* @return the disambiguated {@link Driver}
*/
private Driver findDriver(URI uri, ServiceConfigurationError originalError) {
private Driver<SqlConnectOptions> findDriver(URI uri, ServiceConfigurationError originalError) {
String scheme = scheme( uri );
List<Driver> selected = new ArrayList<>();
for ( Driver d : ServiceLoader.load( Driver.class ) ) {
List<Driver<SqlConnectOptions>> selected = new ArrayList<>();
for ( Driver<SqlConnectOptions> d : ServiceLoader.load( Driver.class ) ) {
String driverName = d.getClass().getCanonicalName();
if ( matchesScheme( driverName, scheme ) ) {
LOG.selectedDriver( driverName );
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@

import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Supplier;

@@ -29,6 +28,7 @@
import org.hibernate.reactive.provider.service.ReactiveGenerationTarget;
import org.hibernate.reactive.stage.Stage;
import org.hibernate.reactive.testing.SessionFactoryManager;
import org.hibernate.reactive.util.impl.CompletionStages;
import org.hibernate.tool.schema.spi.SchemaManagementTool;

import org.junit.jupiter.api.AfterAll;
@@ -41,7 +41,6 @@
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.smallrye.mutiny.Uni;
import io.vertx.core.Promise;
import io.vertx.core.VertxOptions;
import io.vertx.junit5.RunTestOnContext;
import io.vertx.junit5.Timeout;
@@ -75,7 +74,7 @@ public abstract class BaseReactiveTest {
* Configure Vertx JUnit5 test context
*/
@RegisterExtension
static RunTestOnContext testOnContext = new RunTestOnContext( vertxOptions() );
static RunTestOnContext testOnContext = new RunTestOnContext( vertxOptions(), false );

private static VertxOptions vertxOptions() {
Metrics.addRegistry( new SimpleMeterRegistry() );
@@ -209,33 +208,19 @@ protected CompletionStage<Void> setupSessionFactory(Configuration configuration)
* @return a {@link CompletionStage} void that succeeds when the factory is ready.
*/
protected CompletionStage<Void> setupSessionFactory(Supplier<Configuration> confSupplier) {
CompletableFuture<Void> future = new CompletableFuture<>();
testOnContext.vertx()
return testOnContext.vertx()
.executeBlocking(
// schema generation is a blocking operation and so it causes an
// exception when run on the Vert.x event loop. So call it using
// Vertx.executeBlocking()
promise -> startFactoryManager( promise, confSupplier ),
event -> {
if ( event.succeeded() ) {
future.complete( null );
}
else {
future.completeExceptionally( event.cause() );
}
}
);
return future;
}

private void startFactoryManager(Promise<Object> p, Supplier<Configuration> confSupplier) {
try {
factoryManager.start( () -> createHibernateSessionFactory( confSupplier.get() ) );
p.complete();
}
catch (Throwable e) {
p.fail( e );
}
() -> startFactoryManager( confSupplier ),
false
).toCompletionStage().thenCompose( CompletionStages::voidFuture );
}

private Object startFactoryManager(Supplier<Configuration> confSupplier) {
factoryManager.start( () -> createHibernateSessionFactory( confSupplier.get() ) );
return null;
}

private SessionFactory createHibernateSessionFactory(Configuration configuration) {
Original file line number Diff line number Diff line change
@@ -7,16 +7,12 @@

import java.net.URI;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.hibernate.reactive.MyCurrentTenantIdentifierResolver.Tenant;
import org.hibernate.reactive.pool.impl.DefaultSqlClientPool;

import io.vertx.core.AsyncResult;
import io.vertx.core.Context;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.PoolOptions;
@@ -58,8 +54,18 @@ protected Pool createPool(URI uri, SqlConnectOptions connectOptions, PoolOptions
return pools;
}

private Pool createPool(URI uri, SqlConnectOptions connectOptions, PoolOptions poolOptions, Vertx vertx, Tenant tenant) {
return super.createPool( changeDbName( uri, tenant ), changeDbName( connectOptions, tenant ), poolOptions, vertx );
private Pool createPool(
URI uri,
SqlConnectOptions connectOptions,
PoolOptions poolOptions,
Vertx vertx,
Tenant tenant) {
return super.createPool(
changeDbName( uri, tenant ),
changeDbName( connectOptions, tenant ),
poolOptions,
vertx
);
}

/**
@@ -100,11 +106,6 @@ public Pool getTenantPool(Tenant tenantId) {
return poolMap.get( tenantId );
}

@Override
public void getConnection(Handler<AsyncResult<SqlConnection>> handler) {
poolMap.get( defaultTenantId ).getConnection( handler );
}

@Override
public Future<SqlConnection> getConnection() {
return poolMap.get( defaultTenantId ).getConnection();
@@ -125,21 +126,6 @@ public PreparedQuery<RowSet<Row>> preparedQuery(String sql, PrepareOptions optio
return poolMap.get( defaultTenantId ).preparedQuery( sql, options );
}

@Override
public void close(Handler<AsyncResult<Void>> handler) {
poolMap.forEach( (tenant, pool) -> pool.close( handler ) );
}

@Override
public Pool connectHandler(Handler<SqlConnection> handler) {
return poolMap.get( defaultTenantId ).connectHandler( handler );
}

@Override
public Pool connectionProvider(Function<Context, Future<SqlConnection>> provider) {
return poolMap.get( defaultTenantId ).connectionProvider( provider );
}

@Override
public int size() {
return poolMap.get( defaultTenantId ).size();
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@
import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.DB2;
import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.MARIA;
import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.MYSQL;
import static org.hibernate.reactive.testing.ReactiveAssertions.assertThrown;
import static org.hibernate.tool.schema.JdbcMetadaAccessStrategy.GROUPED;
import static org.hibernate.tool.schema.JdbcMetadaAccessStrategy.INDIVIDUALLY;
import static org.junit.jupiter.params.provider.Arguments.arguments;
@@ -126,13 +127,8 @@ context, setupFactory( strategy, type )
validateConf.addAnnotatedClass( BasicTypesTestEntity.class );
// The table mapping this entity shouldn't be in the db
validateConf.addAnnotatedClass( Extra.class );
return setupSessionFactory( validateConf )
.handle( (unused, throwable) -> {
assertThat( throwable )
.isInstanceOf( SchemaManagementException.class )
.hasMessage( errorMessage );
return null;
} );
return assertThrown( SchemaManagementException.class, setupSessionFactory( validateConf ) )
.thenAccept( throwable -> assertThat( throwable ).hasMessage( errorMessage ) );
} )
);
}
Loading