Skip to content

Commit 4782948

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 02aed1b commit 4782948

File tree

1 file changed

+260
-0
lines changed

1 file changed

+260
-0
lines changed
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
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;
42+
import static org.junit.jupiter.params.provider.Arguments.arguments;
43+
44+
/**
45+
* Hibernate ORM allows starting up without access to the DB ("offline")
46+
* when {@link Settings#ALLOW_METADATA_ON_BOOT} is set to false.
47+
* <p>
48+
* Inspired by the test
49+
* {@code org.hibernate.orm.test.boot.database.metadata.MetadataAccessTests}
50+
* in Hibernate ORM.
51+
* </p>
52+
*/
53+
public class MetadataAccessTest {
54+
55+
private static SqlStatementTracker sqlTracker;
56+
57+
private static final int EXPECTED_MAJOR = 123;
58+
private static final int EXPECTED_MINOR = 456;
59+
private static final DatabaseVersion EXPECTED_VERSION = DatabaseVersion.make( EXPECTED_MAJOR, EXPECTED_MINOR );
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, String.valueOf( EXPECTED_MAJOR ) );
65+
dbProperties.setProperty( Settings.DIALECT_DB_MINOR_VERSION, String.valueOf( EXPECTED_MINOR ) );
66+
return dbProperties;
67+
}
68+
69+
private static Properties jakartaMajorMinorProperties() {
70+
Properties dbProperties = new Properties();
71+
dbProperties.setProperty( Settings.JAKARTA_HBM2DDL_DB_MAJOR_VERSION, String.valueOf( EXPECTED_MAJOR ) );
72+
dbProperties.setProperty( Settings.JAKARTA_HBM2DDL_DB_MINOR_VERSION, String.valueOf( EXPECTED_MINOR ) );
73+
return dbProperties;
74+
}
75+
76+
private static Properties jakartaFullDbVersion() {
77+
Properties dbProperties = new Properties();
78+
dbProperties.setProperty( Settings.JAKARTA_HBM2DDL_DB_VERSION, EXPECTED_MAJOR + "." + EXPECTED_MINOR );
79+
return dbProperties;
80+
}
81+
82+
private static Properties dialectFullDbVersion() {
83+
Properties dbProperties = new Properties();
84+
dbProperties.setProperty( Settings.DIALECT_DB_VERSION, EXPECTED_MAJOR + "." + EXPECTED_MINOR );
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+
197+
assertThat( determinedDatabaseVersion ).isEqualTo( metadataEnabledDialect.getVersion() );
198+
assertThat( determinedDatabaseVersion.getMajor() ).isEqualTo( metadataEnabledDialect.getVersion().getMajor() );
199+
assertThat( determinedDatabaseVersion.getMinor() ).isEqualTo( metadataEnabledDialect.getVersion().getMinor() );
200+
assertThat( determinedDatabaseVersion.getMicro() ).isEqualTo( metadataEnabledDialect.getVersion().getMicro() );
201+
}
202+
}
203+
204+
private Configuration constructConfiguration(Properties properties) {
205+
Configuration configuration = new Configuration();
206+
setSqlLoggingProperties( configuration );
207+
configuration.addProperties( properties );
208+
209+
// Construct a tracker that collects query statements via the SqlStatementLogger framework.
210+
// Pass in configuration properties to hand off any actual logging properties
211+
sqlTracker = new SqlStatementTracker( s -> true, configuration.getProperties() );
212+
return configuration;
213+
}
214+
215+
private StandardServiceRegistry createServiceRegistry(Properties properties) {
216+
Configuration configuration = constructConfiguration( properties );
217+
StandardServiceRegistryBuilder builder = new ReactiveServiceRegistryBuilder();
218+
// We will set these properties when needed
219+
assertThat( builder.getSettings() ).doesNotContainKeys( DIALECT, JAKARTA_HBM2DDL_DB_NAME );
220+
builder.applySettings( configuration.getProperties() );
221+
222+
builder.addInitiator( new CapturingDialectFactory.Initiator() );
223+
sqlTracker.registerService( builder );
224+
return builder.enableAutoClose().build();
225+
}
226+
227+
private static Dialect dialect(StandardServiceRegistry registry) {
228+
return registry.getService( JdbcEnvironment.class ).getDialect();
229+
}
230+
231+
private static DialectResolutionInfo dialectResolutionInfo(StandardServiceRegistry registry) {
232+
return ( (CapturingDialectFactory) registry.getService( DialectFactory.class ) )
233+
.capturedDialectResolutionInfoSource.getDialectResolutionInfo();
234+
}
235+
236+
// A hack to easily retrieve DialectResolutionInfo exactly as it would be constructed by Hibernate ORM
237+
private static class CapturingDialectFactory extends DialectFactoryImpl {
238+
239+
static class Initiator implements StandardServiceInitiator<DialectFactory> {
240+
@Override
241+
public Class<DialectFactory> getServiceInitiated() {
242+
return DialectFactory.class;
243+
}
244+
245+
@Override
246+
public DialectFactory initiateService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
247+
return new CapturingDialectFactory();
248+
}
249+
}
250+
251+
DialectResolutionInfoSource capturedDialectResolutionInfoSource;
252+
253+
@Override
254+
public Dialect buildDialect(Map<String, Object> configValues, DialectResolutionInfoSource resolutionInfoSource)
255+
throws HibernateException {
256+
this.capturedDialectResolutionInfoSource = resolutionInfoSource;
257+
return super.buildDialect( configValues, resolutionInfoSource );
258+
}
259+
}
260+
}

0 commit comments

Comments
 (0)