Skip to content

Commit 4de56ad

Browse files
committed
[hibernate#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 3c857e7 commit 4de56ad

File tree

1 file changed

+257
-0
lines changed

1 file changed

+257
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
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+
// Expected version when with set it explicitly in the configuration
58+
private static final DatabaseVersion EXPECTED_VERSION = DatabaseVersion.make( 999, 111 );
59+
60+
private static Properties dialectMajorMinorProperties() {
61+
Properties dbProperties = new Properties();
62+
// Major and Minor should override the full version, so we keep them different
63+
dbProperties.setProperty( Settings.DIALECT_DB_MAJOR_VERSION, "999" );
64+
dbProperties.setProperty( Settings.DIALECT_DB_MINOR_VERSION, "111" );
65+
return dbProperties;
66+
}
67+
68+
private static Properties jakartaMajorMinorProperties() {
69+
Properties dbProperties = new Properties();
70+
dbProperties.setProperty( Settings.JAKARTA_HBM2DDL_DB_MAJOR_VERSION, "999" );
71+
dbProperties.setProperty( Settings.JAKARTA_HBM2DDL_DB_MINOR_VERSION, "111" );
72+
return dbProperties;
73+
}
74+
75+
private static Properties jakartaFullDbVersion() {
76+
Properties dbProperties = new Properties();
77+
dbProperties.setProperty( Settings.JAKARTA_HBM2DDL_DB_VERSION, "999.111" );
78+
return dbProperties;
79+
}
80+
81+
private static Properties dialectFullDbVersion() {
82+
Properties dbProperties = new Properties();
83+
dbProperties.setProperty( Settings.DIALECT_DB_VERSION, "999.111" );
84+
return dbProperties;
85+
}
86+
87+
static Stream<Arguments> explicitVersionProperties() {
88+
return Stream.of(
89+
arguments( "Jakarta properties", jakartaMajorMinorProperties() ),
90+
arguments( "Deprecated dialect properties", dialectMajorMinorProperties() ),
91+
arguments( "Jakarta db version property", jakartaFullDbVersion() ),
92+
arguments( "Deprecated dialect db version property", dialectFullDbVersion() )
93+
);
94+
}
95+
96+
@ParameterizedTest(name = "Test {0} with " + DIALECT)
97+
@MethodSource("explicitVersionProperties")
98+
public void testExplicitVersionWithDialect(String display, Properties dbProperties) {
99+
dbProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" );
100+
dbProperties.setProperty( DIALECT, dbType().getDialectClass().getName() );
101+
102+
try (StandardServiceRegistry serviceRegistry = createServiceRegistry( dbProperties )) {
103+
final Dialect dialect = dialect( serviceRegistry );
104+
assertThat( dialect ).isInstanceOf( dbType().getDialectClass() );
105+
assertThat( dialect.getVersion() ).isEqualTo( EXPECTED_VERSION );
106+
}
107+
108+
assertThat( sqlTracker.getLoggedQueries() )
109+
.as( "No query should be executed at start up" )
110+
.isEmpty();
111+
}
112+
113+
@ParameterizedTest(name = "Test {0} with " + JAKARTA_HBM2DDL_DB_NAME)
114+
@MethodSource("explicitVersionProperties")
115+
public void testExplicitVersionWithJakartaDbName(String display, Properties dbProperties) {
116+
dbProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" );
117+
dbProperties.setProperty( JAKARTA_HBM2DDL_DB_NAME, dbType().getProductName() );
118+
119+
try (StandardServiceRegistry serviceRegistry = createServiceRegistry( dbProperties )) {
120+
final Dialect dialect = dialect( serviceRegistry );
121+
assertThat( dialect ).isInstanceOf( dbType().getDialectClass() );
122+
assertThat( dialect.getVersion() ).isEqualTo( EXPECTED_VERSION );
123+
}
124+
125+
assertThat( sqlTracker.getLoggedQueries() )
126+
.as( "No query should be executed at start up" )
127+
.isEmpty();
128+
}
129+
130+
@Test
131+
public void testMinimumDatabaseVersionWithDialect() {
132+
final Properties dbProperties = new Properties();
133+
dbProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" );
134+
dbProperties.setProperty( DIALECT, dbType().getDialectClass().getName() );
135+
136+
try (StandardServiceRegistry serviceRegistry = createServiceRegistry( dbProperties )) {
137+
final Dialect dialect = dialect( serviceRegistry );
138+
assertThat( dialect ).isInstanceOf( dbType().getDialectClass() );
139+
assertThat( dialect.getVersion() ).isEqualTo( dbType().getMinimumVersion() );
140+
}
141+
142+
assertThat( sqlTracker.getLoggedQueries() )
143+
.as( "No query should be executed at start up" )
144+
.isEmpty();
145+
}
146+
147+
@Test
148+
public void testMinimumDatabaseVersionWithJakartaDbName() {
149+
final Properties dbProperties = new Properties();
150+
dbProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" );
151+
dbProperties.setProperty( JAKARTA_HBM2DDL_DB_NAME, dbType().getProductName() );
152+
153+
try (StandardServiceRegistry serviceRegistry = createServiceRegistry( dbProperties )) {
154+
final Dialect dialect = dialect( serviceRegistry );
155+
assertThat( dialect ).isInstanceOf( dbType().getDialectClass() );
156+
assertThat( dialect.getVersion() ).isEqualTo( dbType().getMinimumVersion() );
157+
}
158+
159+
assertThat( sqlTracker.getLoggedQueries() )
160+
.as( "No query should be executed at start up" )
161+
.isEmpty();
162+
}
163+
164+
@Test
165+
public void testDeterminedVersion() {
166+
final Properties disabledProperties = new Properties();
167+
disabledProperties.setProperty( ALLOW_METADATA_ON_BOOT, "false" );
168+
disabledProperties.setProperty( DIALECT, dbType().getDialectClass().getName() );
169+
final Dialect metadataDisabledDialect;
170+
try (StandardServiceRegistry serviceRegistry = createServiceRegistry( disabledProperties )) {
171+
// The version on this dialect may be anything, but most likely will be the minimum version.
172+
// We're not interested in that, but in how determineDatabaseVersion() behaves for this dialect,
173+
// when passed actual resolution info -- which Quarkus may do.
174+
metadataDisabledDialect = dialect( serviceRegistry );
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 )
197+
.isEqualTo( metadataEnabledDialect.getVersion() );
198+
}
199+
}
200+
201+
private Configuration constructConfiguration(Properties properties) {
202+
Configuration configuration = new Configuration();
203+
setSqlLoggingProperties( configuration );
204+
configuration.addProperties( properties );
205+
206+
// Construct a tracker that collects query statements via the SqlStatementLogger framework.
207+
// Pass in configuration properties to hand off any actual logging properties
208+
sqlTracker = new SqlStatementTracker( s -> true, configuration.getProperties() );
209+
return configuration;
210+
}
211+
212+
private StandardServiceRegistry createServiceRegistry(Properties properties) {
213+
Configuration configuration = constructConfiguration( properties );
214+
StandardServiceRegistryBuilder builder = new ReactiveServiceRegistryBuilder();
215+
// We will set these properties when needed
216+
assertThat( builder.getSettings() ).doesNotContainKeys( DIALECT, JAKARTA_HBM2DDL_DB_NAME );
217+
builder.applySettings( configuration.getProperties() );
218+
219+
builder.addInitiator( new CapturingDialectFactory.Initiator() );
220+
sqlTracker.registerService( builder );
221+
return builder.enableAutoClose().build();
222+
}
223+
224+
private static Dialect dialect(StandardServiceRegistry registry) {
225+
return registry.getService( JdbcEnvironment.class ).getDialect();
226+
}
227+
228+
private static DialectResolutionInfo dialectResolutionInfo(StandardServiceRegistry registry) {
229+
return ( (CapturingDialectFactory) registry.getService( DialectFactory.class ) )
230+
.capturedDialectResolutionInfoSource.getDialectResolutionInfo();
231+
}
232+
233+
// A hack to easily retrieve DialectResolutionInfo exactly as it would be constructed by Hibernate ORM
234+
private static class CapturingDialectFactory extends DialectFactoryImpl {
235+
236+
static class Initiator implements StandardServiceInitiator<DialectFactory> {
237+
@Override
238+
public Class<DialectFactory> getServiceInitiated() {
239+
return DialectFactory.class;
240+
}
241+
242+
@Override
243+
public DialectFactory initiateService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
244+
return new CapturingDialectFactory();
245+
}
246+
}
247+
248+
DialectResolutionInfoSource capturedDialectResolutionInfoSource;
249+
250+
@Override
251+
public Dialect buildDialect(Map<String, Object> configValues, DialectResolutionInfoSource resolutionInfoSource)
252+
throws HibernateException {
253+
this.capturedDialectResolutionInfoSource = resolutionInfoSource;
254+
return super.buildDialect( configValues, resolutionInfoSource );
255+
}
256+
}
257+
}

0 commit comments

Comments
 (0)