From 1e04a1db0a0523dc45789e27342eff6f67776a7d Mon Sep 17 00:00:00 2001 From: blafond Date: Thu, 17 Mar 2022 10:49:19 -0500 Subject: [PATCH] [#929] Draft - Initial changes for H2 db support --- hibernate-reactive-core/build.gradle | 5 + .../DefaultSqlClientPoolConfiguration.java | 7 ++ .../reactive/pool/impl/H2SqlClientPool.java | 109 ++++++++++++++++++ .../service/NoJdbcEnvironmentInitiator.java | 4 + .../hibernate/reactive/BaseReactiveTest.java | 21 +++- .../containers/DatabaseConfiguration.java | 3 +- .../reactive/containers/H2Database.java | 63 ++++++++++ 7 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/H2SqlClientPool.java create mode 100644 hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/H2Database.java diff --git a/hibernate-reactive-core/build.gradle b/hibernate-reactive-core/build.gradle index 54ce89583..3b116f134 100644 --- a/hibernate-reactive-core/build.gradle +++ b/hibernate-reactive-core/build.gradle @@ -26,6 +26,9 @@ dependencies { //Specific implementation details of Hibernate Reactive: implementation "io.vertx:vertx-sql-client:${vertxVersion}" + implementation 'io.agroal:agroal-api:1.12' + implementation 'io.agroal:agroal-pool:1.12' + implementation "io.vertx:vertx-jdbc-client:${vertxVersion}" // Testing testImplementation 'org.assertj:assertj-core:3.20.2' @@ -65,6 +68,8 @@ dependencies { testImplementation "org.testcontainers:cockroachdb:${testcontainersVersion}" testImplementation "org.testcontainers:mssqlserver:${testcontainersVersion}" testImplementation "org.testcontainers:oracle-xe:${testcontainersVersion}" + + testImplementation "com.h2database:h2:2.1.210" } // Print a summary of the results of the tests (number of failures, successes and skipped) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/DefaultSqlClientPoolConfiguration.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/DefaultSqlClientPoolConfiguration.java index 2e114d4bc..412ead56c 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/DefaultSqlClientPoolConfiguration.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/DefaultSqlClientPoolConfiguration.java @@ -92,6 +92,13 @@ public PoolOptions poolOptions() { @Override public SqlConnectOptions connectOptions(URI uri) { String scheme = uri.getScheme(); + if( scheme.equalsIgnoreCase( "h2" )) { + return new SqlConnectOptions() + // username + .setUser("sa") + // password + .setPassword(""); + } String path = scheme.equals( "oracle" ) ? oraclePath( uri ) : uri.getPath(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/H2SqlClientPool.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/H2SqlClientPool.java new file mode 100644 index 000000000..aba2c4cc4 --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/pool/impl/H2SqlClientPool.java @@ -0,0 +1,109 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.pool.impl; + + +import java.net.URI; +import java.util.concurrent.CompletionStage; + +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.jdbc.spi.SqlStatementLogger; +import org.hibernate.reactive.pool.ReactiveConnection; +import org.hibernate.reactive.vertx.VertxInstance; +import org.hibernate.service.spi.ServiceRegistryAwareService; +import org.hibernate.service.spi.ServiceRegistryImplementor; + +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import io.vertx.jdbcclient.JDBCConnectOptions; +import io.vertx.jdbcclient.JDBCPool; +import io.vertx.sqlclient.Pool; +import io.vertx.sqlclient.PoolOptions; +import io.vertx.sqlclient.SqlConnectOptions; +import io.vertx.sqlclient.SqlConnection; + +public class H2SqlClientPool extends SqlClientPool implements ServiceRegistryAwareService { + + //Asynchronous shutdown promise: we can't return it from #close as we implement a + //blocking interface. + private volatile Future closeFuture = Future.succeededFuture(); + + private Pool pools; + private URI uri; + private SqlStatementLogger sqlStatementLogger; + private ServiceRegistryImplementor serviceRegistry; + + public H2SqlClientPool() { + } + + @Override + public void injectServices(ServiceRegistryImplementor serviceRegistry) { + this.serviceRegistry = serviceRegistry; + sqlStatementLogger = serviceRegistry.getService( JdbcServices.class ).getSqlStatementLogger(); + } + + public void start() { + if ( pools == null ) { + pools = createPool( uri ); + } + } + + public void stop() { + if ( pools != null ) { + this.closeFuture = pools.close(); + } + } + + private Pool createPool(URI uri) { + SqlClientPoolConfiguration configuration = serviceRegistry.getService( SqlClientPoolConfiguration.class ); + VertxInstance vertx = serviceRegistry.getService( VertxInstance.class ); + + return createPool( uri, configuration.connectOptions( uri ), configuration.poolOptions(), vertx.getVertx() ); + } + + private Pool createPool(URI uri, SqlConnectOptions connectOptions, PoolOptions poolOptions, Vertx vertx) { + JDBCConnectOptions jdbcOptions = new JDBCConnectOptions(); + jdbcOptions.setUser( connectOptions.getUser() ); + jdbcOptions.setJdbcUrl( "jdbc:" + uri.toString() ); + JDBCPool pool = JDBCPool.pool( vertx, jdbcOptions, poolOptions ); + + return pool; + } + + @Override + protected Pool getPool() { + return pools; + } + + @Override + protected SqlStatementLogger getSqlStatementLogger() { + return sqlStatementLogger; + } + + @Override + public CompletionStage getCloseFuture() { + return closeFuture.toCompletionStage(); + } + + @Override + public CompletionStage getConnection() { + return getConnectionFromPool( getPool() ); + } + + @Override + public CompletionStage getConnection(String tenantId) { + return getConnectionFromPool( getTenantPool( tenantId ) ); + } + + private CompletionStage getConnectionFromPool(Pool pool) { + start(); + return pool.getConnection().toCompletionStage().thenApply( this::newConnection ); + } + + private SqlClientConnection newConnection(SqlConnection connection) { + return new SqlClientConnection( connection, getPool(), getSqlStatementLogger() ); + } +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java index e1975380f..8371a6f02 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java @@ -9,6 +9,7 @@ import org.hibernate.dialect.CockroachDB201Dialect; import org.hibernate.dialect.DB297Dialect; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.MariaDB103Dialect; import org.hibernate.dialect.MySQL8Dialect; import org.hibernate.dialect.Oracle12cDialect; @@ -147,6 +148,9 @@ else if ( url.startsWith( "sqlserver:" ) ) { else if ( url.startsWith( "oracle:" ) ) { return Oracle12cDialect.class; } + else if ( url.startsWith( "h2:" ) ) { + return H2Dialect.class; + } else { return null; } diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BaseReactiveTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BaseReactiveTest.java index aa8a13ab4..16c0d72a0 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BaseReactiveTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/BaseReactiveTest.java @@ -16,10 +16,15 @@ import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.Configuration; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.engine.jdbc.internal.JdbcServicesImpl; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.jdbc.spi.SqlStatementLogger; import org.hibernate.reactive.containers.DatabaseConfiguration; import org.hibernate.reactive.containers.DatabaseConfiguration.DBType; import org.hibernate.reactive.mutiny.Mutiny; import org.hibernate.reactive.pool.ReactiveConnection; +import org.hibernate.reactive.pool.ReactiveConnectionPool; +import org.hibernate.reactive.pool.impl.H2SqlClientPool; import org.hibernate.reactive.provider.ReactiveServiceRegistryBuilder; import org.hibernate.reactive.provider.Settings; import org.hibernate.reactive.provider.service.ReactiveGenerationTarget; @@ -123,6 +128,10 @@ protected Configuration constructConfiguration() { if ( DatabaseConfiguration.dbType() == DBType.DB2 && !doneTablespace ) { configuration.setProperty(Settings.HBM2DDL_IMPORT_FILES, "/db2.sql"); doneTablespace = true; + } else if ( DatabaseConfiguration.dbType() == DBType.H2 ) { + configuration.setProperty(Settings.URL, "jdbc:h2:~/test"); + } else { + configuration.setProperty( Settings.URL, DatabaseConfiguration.getJdbcUrl() ); } //Use JAVA_TOOL_OPTIONS='-Dhibernate.show_sql=true' configuration.setProperty( Settings.SHOW_SQL, System.getProperty(Settings.SHOW_SQL, "false") ); @@ -195,7 +204,17 @@ private SessionFactory createHibernateSessionFactory(Configuration configuration return configuration.buildSessionFactory( registry ); } - protected void addServices(StandardServiceRegistryBuilder builder) {} + protected void addServices(StandardServiceRegistryBuilder builder) { + if(dbType() == DBType.H2 ) { + builder.addService( ReactiveConnectionPool.class, new H2SqlClientPool() ); + builder.addService( JdbcServices.class, new JdbcServicesImpl() { + @Override + public SqlStatementLogger getSqlStatementLogger() { + return new SqlStatementLogger(); + } + } ); + } + } /* * MySQL doesn't implement 'drop table cascade constraints'. diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DatabaseConfiguration.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DatabaseConfiguration.java index 3bd7f11b2..7584ebf38 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DatabaseConfiguration.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/DatabaseConfiguration.java @@ -25,7 +25,8 @@ public enum DBType { POSTGRESQL( PostgreSQLDatabase.INSTANCE, 5432, "POSTGRES", "PG" ), COCKROACHDB( CockroachDBDatabase.INSTANCE, 26257, "COCKROACH" ), SQLSERVER( MSSQLServerDatabase.INSTANCE, 1433, "MSSQL", "MSSQLSERVER" ), - ORACLE( OracleDatabase.INSTANCE, 1521 ); + ORACLE( OracleDatabase.INSTANCE, 1521 ), + H2( H2Database.INSTANCE, -1 ); private final TestableDatabase configuration; private final int defaultPort; diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/H2Database.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/H2Database.java new file mode 100644 index 000000000..a9b15a8bf --- /dev/null +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/H2Database.java @@ -0,0 +1,63 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.containers; + + +import java.util.Map; + + +public class H2Database implements TestableDatabase { + public static H2Database INSTANCE = new H2Database(); + + private String getRegularJdbcUrl() { + return "jdbc:h2:~/test"; + } + + @Override + public String getJdbcUrl() { + return getRegularJdbcUrl(); + } + + @Override + public String getUri() { + { + return "h2:~/test"; + } + } + + @Override + public String getScheme() { + return "h2"; + } + + @Override + public String getNativeDatatypeQuery(String tableName, String columnName) { + throw new UnsupportedOperationException(); + } + + @Override + public String getExpectedNativeDatatype(Class dataType) { + return null; + } + + @Override + public String createJdbcUrl(String host, int port, String database, Map params) { + return getRegularJdbcUrl(); + } + + @Override + public String jdbcStartQuery() { + throw new UnsupportedOperationException(); + } + + @Override + public String jdbcParamDelimiter() { + throw new UnsupportedOperationException(); + } + + private H2Database() { + } +}