Skip to content

Commit

Permalink
[hibernate#929] hibernate-reactive-h2 module
Browse files Browse the repository at this point in the history
  • Loading branch information
blafond committed Mar 31, 2022
1 parent 005ec0b commit 124e8c7
Show file tree
Hide file tree
Showing 11 changed files with 461 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/* 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.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;

import org.hibernate.type.NumericBooleanType;
import org.hibernate.type.TextType;
import org.hibernate.type.TrueFalseType;
import org.hibernate.type.YesNoType;
import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor;

public class H2Database implements TestableDatabase {
public static H2Database INSTANCE = new H2Database();

private static Map<Class<?>, String> expectedDBTypeForClass = new HashMap<>();

static {
{
expectedDBTypeForClass.put( boolean.class, "BOOLEAN" );
expectedDBTypeForClass.put( Boolean.class, "BOOLEAN" );
expectedDBTypeForClass.put( NumericBooleanType.class, "INTEGER" );
expectedDBTypeForClass.put( TrueFalseType.class, "CHARACTER" );
expectedDBTypeForClass.put( YesNoType.class, "CHARACTER" );
expectedDBTypeForClass.put( int.class, "INTEGER" );
expectedDBTypeForClass.put( Integer.class, "INTEGER" );
expectedDBTypeForClass.put( long.class, "BIGINT" );
expectedDBTypeForClass.put( Long.class, "BIGINT" );
expectedDBTypeForClass.put( float.class, "DOUBLE PRECISION" );
expectedDBTypeForClass.put( Float.class, "DOUBLE PRECISION" );
expectedDBTypeForClass.put( double.class, "DOUBLE PRECISION" );
expectedDBTypeForClass.put( Double.class, "DOUBLE PRECISION" );
expectedDBTypeForClass.put( byte.class, "TINYINT" );
expectedDBTypeForClass.put( Byte.class, "TINYINT" );
expectedDBTypeForClass.put( PrimitiveByteArrayTypeDescriptor.class, "BINARY VARYING" );
expectedDBTypeForClass.put( URL.class, "VARCHAR_IGNORECASE" );
expectedDBTypeForClass.put( TimeZone.class, "VARCHAR_IGNORECASE" );
expectedDBTypeForClass.put( Date.class, "DATE" );
expectedDBTypeForClass.put( Timestamp.class, "TIMESTAMP" );
expectedDBTypeForClass.put( Time.class, "TIME" );
expectedDBTypeForClass.put( LocalDate.class, "DATE" );
expectedDBTypeForClass.put( LocalTime.class, "time" );
expectedDBTypeForClass.put( LocalDateTime.class, "TIMESTAMP" );
expectedDBTypeForClass.put( BigInteger.class, "NUMERIC" );
expectedDBTypeForClass.put( BigDecimal.class, "NUMERIC" );
expectedDBTypeForClass.put( Serializable.class, "BINARY VARYING" );
expectedDBTypeForClass.put( UUID.class, "binary" );
expectedDBTypeForClass.put( Instant.class, "datetime" );
expectedDBTypeForClass.put( Duration.class, "bigint" );
expectedDBTypeForClass.put( Character.class, "VARCHAR_IGNORECASE" );
expectedDBTypeForClass.put( char.class, "VARCHAR_IGNORECASE" );
expectedDBTypeForClass.put( TextType.class, "text" );
expectedDBTypeForClass.put( String.class, "VARCHAR_IGNORECASE" );
}
}

private String getRegularJdbcUrl() {
return "jdbc:h2:~/test;DATABASE_TO_UPPER=FALSE";
}

@Override
public String getJdbcUrl() {
return "jdbc:h2:~/test;DATABASE_TO_UPPER=FALSE";
}

@Override
public String getUri() {
return "h2:~/test;DATABASE_TO_UPPER=FALSE";
}


@Override
public String getScheme() {
return "h2:";
}

@Override
public String getNativeDatatypeQuery(String tableName, String columnName) {
return "SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS COLS " +
"WHERE COLS.TABLE_NAME = '" + tableName + "'" +
"AND COLS.COLUMN_NAME= '" + columnName + "'";
}

@Override
public String getExpectedNativeDatatype(Class<?> dataType) {
return expectedDBTypeForClass.get( dataType );
}

@Override
public String createJdbcUrl(String host, int port, String database, Map<String, String> params) {
// Primary mode for H2 is embedded which uses the URL format: "jdbc:h2:~/test"
// H2 can also be configured as a remote server.
// EXAMPLE 1: jdbc:h2:tcp://localhost/D:/myproject/data/project-name
// EXAMPLE 2: jdbc:h2:tcp://localhost/~/test
// EXAMpLE 3: jdbc:h2:tcp://localhost:9081/~/test
final StringBuilder paramsBuilder = new StringBuilder();
if ( params != null && !params.isEmpty() ) {
params.forEach( (key, value) -> {
paramsBuilder.append( jdbcParamDelimiter() );
paramsBuilder.append( key );
paramsBuilder.append( "=" );
paramsBuilder.append( value );
} );
}
String url = "jdbc:" + getScheme() + "//" + host;
if ( port > -1 ) {
url += ":" + port;
}
if ( paramsBuilder.length() > 0 ) {
url += jdbcStartQuery() + paramsBuilder.substring( 1 );
}
if ( database != null ) {
return url + ";database=" + database;
}
return url;
}

@Override
public String jdbcStartQuery() {
return ";";
}

@Override
public String jdbcParamDelimiter() {
return ";";
}

private H2Database() {
}
}
62 changes: 62 additions & 0 deletions hibernate-reactive-h2/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
ext {
mavenPomName = 'Hibernate Reactive H2'
}

description = 'The H2 module of Hibernate Reactive'

apply from: publishScript

dependencies {
implementation project(':hibernate-reactive-core')
implementation "com.h2database:h2:2.1.210"

// Dependencies for using H2 with Vert.x:
implementation "io.vertx:vertx-jdbc-client:${vertxVersion}"
implementation 'io.agroal:agroal-api:1.12'
implementation 'io.agroal:agroal-pool:1.12'

// Testing
testImplementation 'org.assertj:assertj-core:3.20.2'
testImplementation "io.vertx:vertx-unit:${vertxVersion}"

// log4j
testRuntimeOnly 'org.apache.logging.log4j:log4j-core:2.17.1'
}

// Print a summary of the results of the tests (number of failures, successes and skipped)
def loggingSummary(db, result, desc) {
if ( !desc.parent ) { // will match the outermost suite
def output = "${db} results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)"
def repeatLength = output.length() + 1
logger.lifecycle '\n' + ('-' * repeatLength) + '\n' + output + '\n' + ('-' * repeatLength)
}
}

// Configuration for the tests
tasks.withType(Test) {
defaultCharacterEncoding = "UTF-8"
testLogging {
displayGranularity 1
showStandardStreams = project.hasProperty('showStandardOutput')
showStackTraces = true
exceptionFormat = 'full'
events 'PASSED', 'FAILED', 'SKIPPED'
}
systemProperty 'org.hibernate.reactive.common.InternalStateAssertions.ENFORCE', 'true'

if ( project.hasProperty( 'includeTests' ) ) {
// Example: ./gradlew testAll -PincludeTests=DefaultPortTest
filter {
includeTestsMatching project.getProperty( 'includeTests' ) ?: '*'
}
}
}

test {
afterSuite { desc, result ->
loggingSummary( 'H2', result, desc )
}
doFirst {
systemProperty 'db', 'H2'
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.reactive.boot.impl;

import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.reactive.pool.impl.H2ReactiveConnectionPoolInitiator;
import org.hibernate.service.spi.ServiceContributor;

public class H2ServiceContributor implements ServiceContributor {
@Override
public void contribute(StandardServiceRegistryBuilder serviceRegistryBuilder) {
serviceRegistryBuilder.addInitiator( H2ReactiveConnectionPoolInitiator.INSTANCE );
serviceRegistryBuilder.addInitiator( JdbcSqlClientPoolConfigurationInitiator.INSTANCE );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.reactive.boot.impl;

import java.lang.invoke.MethodHandles;
import java.util.Map;

import org.hibernate.boot.registry.StandardServiceInitiator;
import org.hibernate.reactive.logging.impl.Log;
import org.hibernate.reactive.logging.impl.LoggerFactory;
import org.hibernate.reactive.pool.impl.H2ClientPoolConfiguration;
import org.hibernate.reactive.pool.impl.JdbcClientPoolConfiguration;
import org.hibernate.service.spi.ServiceRegistryImplementor;

// TODO: Not sure if we really need this service
public class JdbcSqlClientPoolConfigurationInitiator implements StandardServiceInitiator<JdbcClientPoolConfiguration> {

private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() );

public static final JdbcSqlClientPoolConfigurationInitiator INSTANCE = new JdbcSqlClientPoolConfigurationInitiator() {
};

@Override
public JdbcClientPoolConfiguration initiateService(Map configurationValues, ServiceRegistryImplementor registry) {
return new H2ClientPoolConfiguration();
}

@Override
public Class<JdbcClientPoolConfiguration> getServiceInitiated() {
return JdbcClientPoolConfiguration.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
// * Hibernate Reactive is an adaptation of Hibernate ORM to the
// * world of reactive programming, and replaces JDBC for database
// * access with a non-blocking database client.
// * <p>
// * <p>
*/
package org.hibernate.reactive;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* 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 io.vertx.jdbcclient.JDBCConnectOptions;

public class H2ClientPoolConfiguration extends DefaultSqlClientPoolConfiguration
implements JdbcClientPoolConfiguration {

@Override
public JDBCConnectOptions jdbcConnectOptions(URI uri) {

return new JDBCConnectOptions()
// H2 connection string
.setJdbcUrl( uri.toString() )
// username
.setUser( getUser() == null ? "SA" : getUser() )
// password
.setPassword( getPassword() );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* 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.util.Map;

import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.reactive.pool.ReactiveConnectionPool;
import org.hibernate.reactive.provider.Settings;
import org.hibernate.service.spi.ServiceRegistryImplementor;

public class H2ReactiveConnectionPoolInitiator extends ReactiveConnectionPoolInitiator {

public static final H2ReactiveConnectionPoolInitiator INSTANCE = new H2ReactiveConnectionPoolInitiator();

@Override
public ReactiveConnectionPool initiateService(Map configurationValues, ServiceRegistryImplementor registry) {
String url = ConfigurationHelper.getString( Settings.URL, configurationValues );
// Check URL for H2 and return H2 specific pool
if ( url.startsWith( "jdbc:h2:" ) ) {
return new H2SqlClientPool();
}

// delegate to super class to initiate the DefaultSqlClientPool
return super.initiateService( configurationValues, registry );
}
}
Loading

0 comments on commit 124e8c7

Please sign in to comment.