Skip to content

Commit 124e8c7

Browse files
committed
[hibernate#929] hibernate-reactive-h2 module
1 parent 005ec0b commit 124e8c7

File tree

11 files changed

+461
-0
lines changed

11 files changed

+461
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive.containers;
7+
8+
import java.io.Serializable;
9+
import java.math.BigDecimal;
10+
import java.math.BigInteger;
11+
import java.net.URL;
12+
import java.sql.Time;
13+
import java.sql.Timestamp;
14+
import java.time.Duration;
15+
import java.time.Instant;
16+
import java.time.LocalDate;
17+
import java.time.LocalDateTime;
18+
import java.time.LocalTime;
19+
import java.util.Date;
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
import java.util.TimeZone;
23+
import java.util.UUID;
24+
25+
import org.hibernate.type.NumericBooleanType;
26+
import org.hibernate.type.TextType;
27+
import org.hibernate.type.TrueFalseType;
28+
import org.hibernate.type.YesNoType;
29+
import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor;
30+
31+
public class H2Database implements TestableDatabase {
32+
public static H2Database INSTANCE = new H2Database();
33+
34+
private static Map<Class<?>, String> expectedDBTypeForClass = new HashMap<>();
35+
36+
static {
37+
{
38+
expectedDBTypeForClass.put( boolean.class, "BOOLEAN" );
39+
expectedDBTypeForClass.put( Boolean.class, "BOOLEAN" );
40+
expectedDBTypeForClass.put( NumericBooleanType.class, "INTEGER" );
41+
expectedDBTypeForClass.put( TrueFalseType.class, "CHARACTER" );
42+
expectedDBTypeForClass.put( YesNoType.class, "CHARACTER" );
43+
expectedDBTypeForClass.put( int.class, "INTEGER" );
44+
expectedDBTypeForClass.put( Integer.class, "INTEGER" );
45+
expectedDBTypeForClass.put( long.class, "BIGINT" );
46+
expectedDBTypeForClass.put( Long.class, "BIGINT" );
47+
expectedDBTypeForClass.put( float.class, "DOUBLE PRECISION" );
48+
expectedDBTypeForClass.put( Float.class, "DOUBLE PRECISION" );
49+
expectedDBTypeForClass.put( double.class, "DOUBLE PRECISION" );
50+
expectedDBTypeForClass.put( Double.class, "DOUBLE PRECISION" );
51+
expectedDBTypeForClass.put( byte.class, "TINYINT" );
52+
expectedDBTypeForClass.put( Byte.class, "TINYINT" );
53+
expectedDBTypeForClass.put( PrimitiveByteArrayTypeDescriptor.class, "BINARY VARYING" );
54+
expectedDBTypeForClass.put( URL.class, "VARCHAR_IGNORECASE" );
55+
expectedDBTypeForClass.put( TimeZone.class, "VARCHAR_IGNORECASE" );
56+
expectedDBTypeForClass.put( Date.class, "DATE" );
57+
expectedDBTypeForClass.put( Timestamp.class, "TIMESTAMP" );
58+
expectedDBTypeForClass.put( Time.class, "TIME" );
59+
expectedDBTypeForClass.put( LocalDate.class, "DATE" );
60+
expectedDBTypeForClass.put( LocalTime.class, "time" );
61+
expectedDBTypeForClass.put( LocalDateTime.class, "TIMESTAMP" );
62+
expectedDBTypeForClass.put( BigInteger.class, "NUMERIC" );
63+
expectedDBTypeForClass.put( BigDecimal.class, "NUMERIC" );
64+
expectedDBTypeForClass.put( Serializable.class, "BINARY VARYING" );
65+
expectedDBTypeForClass.put( UUID.class, "binary" );
66+
expectedDBTypeForClass.put( Instant.class, "datetime" );
67+
expectedDBTypeForClass.put( Duration.class, "bigint" );
68+
expectedDBTypeForClass.put( Character.class, "VARCHAR_IGNORECASE" );
69+
expectedDBTypeForClass.put( char.class, "VARCHAR_IGNORECASE" );
70+
expectedDBTypeForClass.put( TextType.class, "text" );
71+
expectedDBTypeForClass.put( String.class, "VARCHAR_IGNORECASE" );
72+
}
73+
}
74+
75+
private String getRegularJdbcUrl() {
76+
return "jdbc:h2:~/test;DATABASE_TO_UPPER=FALSE";
77+
}
78+
79+
@Override
80+
public String getJdbcUrl() {
81+
return "jdbc:h2:~/test;DATABASE_TO_UPPER=FALSE";
82+
}
83+
84+
@Override
85+
public String getUri() {
86+
return "h2:~/test;DATABASE_TO_UPPER=FALSE";
87+
}
88+
89+
90+
@Override
91+
public String getScheme() {
92+
return "h2:";
93+
}
94+
95+
@Override
96+
public String getNativeDatatypeQuery(String tableName, String columnName) {
97+
return "SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS COLS " +
98+
"WHERE COLS.TABLE_NAME = '" + tableName + "'" +
99+
"AND COLS.COLUMN_NAME= '" + columnName + "'";
100+
}
101+
102+
@Override
103+
public String getExpectedNativeDatatype(Class<?> dataType) {
104+
return expectedDBTypeForClass.get( dataType );
105+
}
106+
107+
@Override
108+
public String createJdbcUrl(String host, int port, String database, Map<String, String> params) {
109+
// Primary mode for H2 is embedded which uses the URL format: "jdbc:h2:~/test"
110+
// H2 can also be configured as a remote server.
111+
// EXAMPLE 1: jdbc:h2:tcp://localhost/D:/myproject/data/project-name
112+
// EXAMPLE 2: jdbc:h2:tcp://localhost/~/test
113+
// EXAMpLE 3: jdbc:h2:tcp://localhost:9081/~/test
114+
final StringBuilder paramsBuilder = new StringBuilder();
115+
if ( params != null && !params.isEmpty() ) {
116+
params.forEach( (key, value) -> {
117+
paramsBuilder.append( jdbcParamDelimiter() );
118+
paramsBuilder.append( key );
119+
paramsBuilder.append( "=" );
120+
paramsBuilder.append( value );
121+
} );
122+
}
123+
String url = "jdbc:" + getScheme() + "//" + host;
124+
if ( port > -1 ) {
125+
url += ":" + port;
126+
}
127+
if ( paramsBuilder.length() > 0 ) {
128+
url += jdbcStartQuery() + paramsBuilder.substring( 1 );
129+
}
130+
if ( database != null ) {
131+
return url + ";database=" + database;
132+
}
133+
return url;
134+
}
135+
136+
@Override
137+
public String jdbcStartQuery() {
138+
return ";";
139+
}
140+
141+
@Override
142+
public String jdbcParamDelimiter() {
143+
return ";";
144+
}
145+
146+
private H2Database() {
147+
}
148+
}

Diff for: hibernate-reactive-h2/build.gradle

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
ext {
2+
mavenPomName = 'Hibernate Reactive H2'
3+
}
4+
5+
description = 'The H2 module of Hibernate Reactive'
6+
7+
apply from: publishScript
8+
9+
dependencies {
10+
implementation project(':hibernate-reactive-core')
11+
implementation "com.h2database:h2:2.1.210"
12+
13+
// Dependencies for using H2 with Vert.x:
14+
implementation "io.vertx:vertx-jdbc-client:${vertxVersion}"
15+
implementation 'io.agroal:agroal-api:1.12'
16+
implementation 'io.agroal:agroal-pool:1.12'
17+
18+
// Testing
19+
testImplementation 'org.assertj:assertj-core:3.20.2'
20+
testImplementation "io.vertx:vertx-unit:${vertxVersion}"
21+
22+
// log4j
23+
testRuntimeOnly 'org.apache.logging.log4j:log4j-core:2.17.1'
24+
}
25+
26+
// Print a summary of the results of the tests (number of failures, successes and skipped)
27+
def loggingSummary(db, result, desc) {
28+
if ( !desc.parent ) { // will match the outermost suite
29+
def output = "${db} results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)"
30+
def repeatLength = output.length() + 1
31+
logger.lifecycle '\n' + ('-' * repeatLength) + '\n' + output + '\n' + ('-' * repeatLength)
32+
}
33+
}
34+
35+
// Configuration for the tests
36+
tasks.withType(Test) {
37+
defaultCharacterEncoding = "UTF-8"
38+
testLogging {
39+
displayGranularity 1
40+
showStandardStreams = project.hasProperty('showStandardOutput')
41+
showStackTraces = true
42+
exceptionFormat = 'full'
43+
events 'PASSED', 'FAILED', 'SKIPPED'
44+
}
45+
systemProperty 'org.hibernate.reactive.common.InternalStateAssertions.ENFORCE', 'true'
46+
47+
if ( project.hasProperty( 'includeTests' ) ) {
48+
// Example: ./gradlew testAll -PincludeTests=DefaultPortTest
49+
filter {
50+
includeTestsMatching project.getProperty( 'includeTests' ) ?: '*'
51+
}
52+
}
53+
}
54+
55+
test {
56+
afterSuite { desc, result ->
57+
loggingSummary( 'H2', result, desc )
58+
}
59+
doFirst {
60+
systemProperty 'db', 'H2'
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive.boot.impl;
7+
8+
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
9+
import org.hibernate.reactive.pool.impl.H2ReactiveConnectionPoolInitiator;
10+
import org.hibernate.service.spi.ServiceContributor;
11+
12+
public class H2ServiceContributor implements ServiceContributor {
13+
@Override
14+
public void contribute(StandardServiceRegistryBuilder serviceRegistryBuilder) {
15+
serviceRegistryBuilder.addInitiator( H2ReactiveConnectionPoolInitiator.INSTANCE );
16+
serviceRegistryBuilder.addInitiator( JdbcSqlClientPoolConfigurationInitiator.INSTANCE );
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive.boot.impl;
7+
8+
import java.lang.invoke.MethodHandles;
9+
import java.util.Map;
10+
11+
import org.hibernate.boot.registry.StandardServiceInitiator;
12+
import org.hibernate.reactive.logging.impl.Log;
13+
import org.hibernate.reactive.logging.impl.LoggerFactory;
14+
import org.hibernate.reactive.pool.impl.H2ClientPoolConfiguration;
15+
import org.hibernate.reactive.pool.impl.JdbcClientPoolConfiguration;
16+
import org.hibernate.service.spi.ServiceRegistryImplementor;
17+
18+
// TODO: Not sure if we really need this service
19+
public class JdbcSqlClientPoolConfigurationInitiator implements StandardServiceInitiator<JdbcClientPoolConfiguration> {
20+
21+
private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() );
22+
23+
public static final JdbcSqlClientPoolConfigurationInitiator INSTANCE = new JdbcSqlClientPoolConfigurationInitiator() {
24+
};
25+
26+
@Override
27+
public JdbcClientPoolConfiguration initiateService(Map configurationValues, ServiceRegistryImplementor registry) {
28+
return new H2ClientPoolConfiguration();
29+
}
30+
31+
@Override
32+
public Class<JdbcClientPoolConfiguration> getServiceInitiated() {
33+
return JdbcClientPoolConfiguration.class;
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
// * Hibernate Reactive is an adaptation of Hibernate ORM to the
3+
// * world of reactive programming, and replaces JDBC for database
4+
// * access with a non-blocking database client.
5+
// * <p>
6+
// * <p>
7+
*/
8+
package org.hibernate.reactive;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive.pool.impl;
7+
8+
import java.net.URI;
9+
10+
import io.vertx.jdbcclient.JDBCConnectOptions;
11+
12+
public class H2ClientPoolConfiguration extends DefaultSqlClientPoolConfiguration
13+
implements JdbcClientPoolConfiguration {
14+
15+
@Override
16+
public JDBCConnectOptions jdbcConnectOptions(URI uri) {
17+
18+
return new JDBCConnectOptions()
19+
// H2 connection string
20+
.setJdbcUrl( uri.toString() )
21+
// username
22+
.setUser( getUser() == null ? "SA" : getUser() )
23+
// password
24+
.setPassword( getPassword() );
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive.pool.impl;
7+
8+
import java.util.Map;
9+
10+
import org.hibernate.internal.util.config.ConfigurationHelper;
11+
import org.hibernate.reactive.pool.ReactiveConnectionPool;
12+
import org.hibernate.reactive.provider.Settings;
13+
import org.hibernate.service.spi.ServiceRegistryImplementor;
14+
15+
public class H2ReactiveConnectionPoolInitiator extends ReactiveConnectionPoolInitiator {
16+
17+
public static final H2ReactiveConnectionPoolInitiator INSTANCE = new H2ReactiveConnectionPoolInitiator();
18+
19+
@Override
20+
public ReactiveConnectionPool initiateService(Map configurationValues, ServiceRegistryImplementor registry) {
21+
String url = ConfigurationHelper.getString( Settings.URL, configurationValues );
22+
// Check URL for H2 and return H2 specific pool
23+
if ( url.startsWith( "jdbc:h2:" ) ) {
24+
return new H2SqlClientPool();
25+
}
26+
27+
// delegate to super class to initiate the DefaultSqlClientPool
28+
return super.initiateService( configurationValues, registry );
29+
}
30+
}

0 commit comments

Comments
 (0)