Skip to content

Commit 2c8b85a

Browse files
committed
[#2129] Test hibernate.boot.allow_jdbc_metadata_access
Hibernate Reactive should be able to start even if there's no database when `hibernate.boot.allow_jdbc_metadata_access = false`.
1 parent abc7715 commit 2c8b85a

File tree

1 file changed

+262
-0
lines changed

1 file changed

+262
-0
lines changed
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
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;
7+
8+
import org.hibernate.HibernateException;
9+
import org.hibernate.boot.registry.StandardServiceInitiator;
10+
import org.hibernate.boot.registry.StandardServiceRegistry;
11+
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
12+
import org.hibernate.cfg.Configuration;
13+
import org.hibernate.dialect.DatabaseVersion;
14+
import org.hibernate.dialect.Dialect;
15+
import org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl;
16+
import org.hibernate.engine.jdbc.dialect.spi.DialectFactory;
17+
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
18+
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfoSource;
19+
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
20+
import org.hibernate.reactive.containers.DatabaseConfiguration;
21+
import org.hibernate.reactive.provider.ReactiveServiceRegistryBuilder;
22+
import org.hibernate.reactive.provider.Settings;
23+
import org.hibernate.reactive.testing.SqlStatementTracker;
24+
import org.hibernate.service.spi.ServiceRegistryImplementor;
25+
26+
import org.junit.jupiter.api.Test;
27+
import org.junit.jupiter.params.ParameterizedTest;
28+
import org.junit.jupiter.params.provider.Arguments;
29+
import org.junit.jupiter.params.provider.MethodSource;
30+
31+
import java.util.Map;
32+
import java.util.Properties;
33+
import java.util.stream.Stream;
34+
35+
import static org.assertj.core.api.Assertions.assertThat;
36+
import static org.hibernate.cfg.JdbcSettings.ALLOW_METADATA_ON_BOOT;
37+
import static org.hibernate.cfg.JdbcSettings.DIALECT;
38+
import static org.hibernate.cfg.JdbcSettings.JAKARTA_HBM2DDL_DB_NAME;
39+
import static org.hibernate.cfg.JdbcSettings.JAKARTA_JDBC_URL;
40+
import static org.hibernate.reactive.BaseReactiveTest.setSqlLoggingProperties;
41+
import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.MARIA;
42+
import static org.hibernate.reactive.containers.DatabaseConfiguration.dbType;
43+
import static org.junit.jupiter.params.provider.Arguments.arguments;
44+
45+
/**
46+
* Hibernate ORM allows starting up without access to the DB ("offline")
47+
* when {@link Settings#ALLOW_METADATA_ON_BOOT} is set to false.
48+
* <p>
49+
* Inspired by the test
50+
* {@code org.hibernate.orm.test.boot.database.metadata.MetadataAccessTests}
51+
* in Hibernate ORM.
52+
* </p>
53+
*/
54+
public class MetadataAccessTest {
55+
56+
private static SqlStatementTracker sqlTracker;
57+
58+
// Expected version when with set it explicitly in the configuration
59+
private static final DatabaseVersion EXPECTED_VERSION = DatabaseVersion.make( 999, 111 );
60+
61+
private static Properties dialectMajorMinorProperties() {
62+
Properties dbProperties = new Properties();
63+
// Major and Minor should override the full version, so we keep them different
64+
dbProperties.setProperty( Settings.DIALECT_DB_MAJOR_VERSION, "999" );
65+
dbProperties.setProperty( Settings.DIALECT_DB_MINOR_VERSION, "111" );
66+
return dbProperties;
67+
}
68+
69+
private static Properties jakartaMajorMinorProperties() {
70+
Properties dbProperties = new Properties();
71+
dbProperties.setProperty( Settings.JAKARTA_HBM2DDL_DB_MAJOR_VERSION, "999" );
72+
dbProperties.setProperty( Settings.JAKARTA_HBM2DDL_DB_MINOR_VERSION, "111" );
73+
return dbProperties;
74+
}
75+
76+
private static Properties jakartaFullDbVersion() {
77+
Properties dbProperties = new Properties();
78+
dbProperties.setProperty( Settings.JAKARTA_HBM2DDL_DB_VERSION, "999.111" );
79+
return dbProperties;
80+
}
81+
82+
private static Properties dialectFullDbVersion() {
83+
Properties dbProperties = new Properties();
84+
dbProperties.setProperty( Settings.DIALECT_DB_VERSION, "999.111" );
85+
return dbProperties;
86+
}
87+
88+
static Stream<Arguments> explicitVersionProperties() {
89+
return Stream.of(
90+
arguments( "Jakarta properties", jakartaMajorMinorProperties() ),
91+
arguments( "Deprecated dialect properties", dialectMajorMinorProperties() ),
92+
arguments( "Jakarta db version property", jakartaFullDbVersion() ),
93+
arguments( "Deprecated dialect db version property", dialectFullDbVersion() )
94+
);
95+
}
96+
97+
@ParameterizedTest(name = "Test {0} with " + DIALECT)
98+
@MethodSource("explicitVersionProperties")
99+
public void testExplicitVersionWithDialect(String display, Properties dbProperties) {
100+
dbProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" );
101+
dbProperties.setProperty( DIALECT, dbType().getDialectClass().getName() );
102+
103+
try (StandardServiceRegistry serviceRegistry = createServiceRegistry( dbProperties )) {
104+
final Dialect dialect = dialect( serviceRegistry );
105+
assertThat( dialect ).isInstanceOf( dbType().getDialectClass() );
106+
assertThat( dialect.getVersion() ).isEqualTo( EXPECTED_VERSION );
107+
}
108+
109+
assertThat( sqlTracker.getLoggedQueries() )
110+
.as( "No query should be executed at start up" )
111+
.isEmpty();
112+
}
113+
114+
@ParameterizedTest(name = "Test {0} with " + JAKARTA_HBM2DDL_DB_NAME)
115+
@MethodSource("explicitVersionProperties")
116+
public void testExplicitVersionWithJakartaDbName(String display, Properties dbProperties) {
117+
dbProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" );
118+
dbProperties.setProperty( JAKARTA_HBM2DDL_DB_NAME, dbType().getProductName() );
119+
120+
try (StandardServiceRegistry serviceRegistry = createServiceRegistry( dbProperties )) {
121+
final Dialect dialect = dialect( serviceRegistry );
122+
assertThat( dialect ).isInstanceOf( dbType().getDialectClass() );
123+
assertThat( dialect.getVersion() ).isEqualTo( EXPECTED_VERSION );
124+
}
125+
126+
assertThat( sqlTracker.getLoggedQueries() )
127+
.as( "No query should be executed at start up" )
128+
.isEmpty();
129+
}
130+
131+
@Test
132+
public void testMinimumDatabaseVersionWithDialect() {
133+
final Properties dbProperties = new Properties();
134+
dbProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" );
135+
dbProperties.setProperty( DIALECT, dbType().getDialectClass().getName() );
136+
137+
try (StandardServiceRegistry serviceRegistry = createServiceRegistry( dbProperties )) {
138+
final Dialect dialect = dialect( serviceRegistry );
139+
assertThat( dialect ).isInstanceOf( dbType().getDialectClass() );
140+
assertThat( dialect.getVersion() ).isEqualTo( dbType().getMinimumVersion() );
141+
}
142+
143+
assertThat( sqlTracker.getLoggedQueries() )
144+
.as( "No query should be executed at start up" )
145+
.isEmpty();
146+
}
147+
148+
@Test
149+
public void testMinimumDatabaseVersionWithJakartaDbName() {
150+
final Properties dbProperties = new Properties();
151+
dbProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" );
152+
dbProperties.setProperty( JAKARTA_HBM2DDL_DB_NAME, dbType().getProductName() );
153+
154+
try (StandardServiceRegistry serviceRegistry = createServiceRegistry( dbProperties )) {
155+
final Dialect dialect = dialect( serviceRegistry );
156+
assertThat( dialect ).isInstanceOf( dbType().getDialectClass() );
157+
assertThat( dialect.getVersion() ).isEqualTo( dbType().getMinimumVersion() );
158+
}
159+
160+
assertThat( sqlTracker.getLoggedQueries() )
161+
.as( "No query should be executed at start up" )
162+
.isEmpty();
163+
}
164+
165+
@Test
166+
public void testDeterminedVersion() {
167+
final Properties disabledProperties = new Properties();
168+
disabledProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" );
169+
disabledProperties.setProperty( DIALECT, dbType().getDialectClass().getName() );
170+
// The dialect when ALLOW_METADATA_ON_BOOT si set to false
171+
final Dialect metadataDisabledDialect;
172+
try (StandardServiceRegistry serviceRegistry = createServiceRegistry( disabledProperties )) {
173+
metadataDisabledDialect = dialect( serviceRegistry );
174+
// We didn't set the version anywhere else, so we expect it to be the minimum version
175+
assertThat( metadataDisabledDialect.getVersion() ).isEqualTo( dbType().getMinimumVersion() );
176+
}
177+
178+
assertThat( sqlTracker.getLoggedQueries() )
179+
.as( "No query should be executed at start up" )
180+
.isEmpty();
181+
182+
final Properties enabledProperties = new Properties();
183+
enabledProperties.setProperty( ALLOW_METADATA_ON_BOOT, "true" );
184+
enabledProperties.setProperty( JAKARTA_JDBC_URL, DatabaseConfiguration.getJdbcUrl() );
185+
try (StandardServiceRegistry serviceRegistry = createServiceRegistry( enabledProperties )) {
186+
final Dialect metadataEnabledDialect = dialect( serviceRegistry );
187+
188+
// We expect determineDatabaseVersion(), when called on metadataAccessDisabledDialect,
189+
// to return the version that would have been returned,
190+
// had we booted up with auto-detection of version (metadata access allowed).
191+
DatabaseVersion determinedDatabaseVersion = metadataDisabledDialect
192+
.determineDatabaseVersion( dialectResolutionInfo( serviceRegistry ) );
193+
194+
// Whatever the version, we don't expect the minimum one
195+
assertThat( determinedDatabaseVersion ).isNotEqualTo( dbType().getMinimumVersion() );
196+
assertThat( determinedDatabaseVersion.getMajor() ).isEqualTo( metadataEnabledDialect.getVersion().getMajor() );
197+
assertThat( determinedDatabaseVersion.getMinor() ).isEqualTo( metadataEnabledDialect.getVersion().getMinor() );
198+
if ( dbType() != MARIA ) {
199+
// MariaDB version from metadata doesn't include the micro
200+
assertThat( determinedDatabaseVersion.getMicro() ).isEqualTo( metadataEnabledDialect.getVersion().getMicro() );
201+
assertThat( determinedDatabaseVersion ).isEqualTo( metadataEnabledDialect.getVersion() );
202+
}
203+
}
204+
}
205+
206+
private Configuration constructConfiguration(Properties properties) {
207+
Configuration configuration = new Configuration();
208+
setSqlLoggingProperties( configuration );
209+
configuration.addProperties( properties );
210+
211+
// Construct a tracker that collects query statements via the SqlStatementLogger framework.
212+
// Pass in configuration properties to hand off any actual logging properties
213+
sqlTracker = new SqlStatementTracker( s -> true, configuration.getProperties() );
214+
return configuration;
215+
}
216+
217+
private StandardServiceRegistry createServiceRegistry(Properties properties) {
218+
Configuration configuration = constructConfiguration( properties );
219+
StandardServiceRegistryBuilder builder = new ReactiveServiceRegistryBuilder();
220+
// We will set these properties when needed
221+
assertThat( builder.getSettings() ).doesNotContainKeys( DIALECT, JAKARTA_HBM2DDL_DB_NAME );
222+
builder.applySettings( configuration.getProperties() );
223+
224+
builder.addInitiator( new CapturingDialectFactory.Initiator() );
225+
sqlTracker.registerService( builder );
226+
return builder.enableAutoClose().build();
227+
}
228+
229+
private static Dialect dialect(StandardServiceRegistry registry) {
230+
return registry.getService( JdbcEnvironment.class ).getDialect();
231+
}
232+
233+
private static DialectResolutionInfo dialectResolutionInfo(StandardServiceRegistry registry) {
234+
return ( (CapturingDialectFactory) registry.getService( DialectFactory.class ) )
235+
.capturedDialectResolutionInfoSource.getDialectResolutionInfo();
236+
}
237+
238+
// A hack to easily retrieve DialectResolutionInfo exactly as it would be constructed by Hibernate ORM
239+
private static class CapturingDialectFactory extends DialectFactoryImpl {
240+
241+
static class Initiator implements StandardServiceInitiator<DialectFactory> {
242+
@Override
243+
public Class<DialectFactory> getServiceInitiated() {
244+
return DialectFactory.class;
245+
}
246+
247+
@Override
248+
public DialectFactory initiateService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
249+
return new CapturingDialectFactory();
250+
}
251+
}
252+
253+
DialectResolutionInfoSource capturedDialectResolutionInfoSource;
254+
255+
@Override
256+
public Dialect buildDialect(Map<String, Object> configValues, DialectResolutionInfoSource resolutionInfoSource)
257+
throws HibernateException {
258+
this.capturedDialectResolutionInfoSource = resolutionInfoSource;
259+
return super.buildDialect( configValues, resolutionInfoSource );
260+
}
261+
}
262+
}

0 commit comments

Comments
 (0)