5
5
*/
6
6
package org .hibernate .reactive .provider .service ;
7
7
8
- import java .util .Map ;
9
- import java .util .concurrent .CompletionStage ;
10
-
11
8
import org .hibernate .boot .registry .StandardServiceInitiator ;
9
+ import org .hibernate .dialect .DatabaseVersion ;
12
10
import org .hibernate .dialect .Dialect ;
11
+ import org .hibernate .engine .jdbc .connections .spi .DatabaseConnectionInfo ;
13
12
import org .hibernate .engine .jdbc .dialect .spi .DialectFactory ;
14
13
import org .hibernate .engine .jdbc .dialect .spi .DialectResolutionInfo ;
15
14
import org .hibernate .engine .jdbc .env .internal .JdbcEnvironmentImpl ;
15
+ import org .hibernate .engine .jdbc .env .internal .JdbcEnvironmentInitiator ;
16
16
import org .hibernate .engine .jdbc .env .spi .JdbcEnvironment ;
17
17
import org .hibernate .engine .jdbc .spi .SqlExceptionHelper ;
18
18
import org .hibernate .reactive .pool .ReactiveConnection ;
19
19
import org .hibernate .reactive .pool .ReactiveConnectionPool ;
20
- import org .hibernate .reactive .provider .Settings ;
21
20
import org .hibernate .reactive .util .impl .CompletionStages ;
22
21
import org .hibernate .service .ServiceRegistry ;
23
22
import org .hibernate .service .spi .ServiceRegistryImplementor ;
24
23
25
24
import io .vertx .sqlclient .spi .DatabaseMetadata ;
25
+ import java .util .Map ;
26
+ import java .util .concurrent .CompletionStage ;
27
+ import java .util .function .Function ;
26
28
29
+ import static java .util .Objects .requireNonNullElse ;
27
30
import static java .util .function .Function .identity ;
28
31
import static org .hibernate .reactive .util .impl .CompletionStages .completedFuture ;
29
32
32
35
* that provides an implementation of {@link JdbcEnvironment} that infers
33
36
* the Hibernate {@link org.hibernate.dialect.Dialect} from the JDBC URL.
34
37
*/
35
- public class NoJdbcEnvironmentInitiator implements StandardServiceInitiator <JdbcEnvironment > {
38
+ public class NoJdbcEnvironmentInitiator extends JdbcEnvironmentInitiator
39
+ implements StandardServiceInitiator <JdbcEnvironment > {
36
40
37
41
public static final NoJdbcEnvironmentInitiator INSTANCE = new NoJdbcEnvironmentInitiator ();
38
42
@@ -42,14 +46,59 @@ public Class<JdbcEnvironment> getServiceInitiated() {
42
46
}
43
47
44
48
@ Override
45
- public JdbcEnvironment initiateService (Map <String , Object > configurationValues , ServiceRegistryImplementor registry ) {
46
- boolean explicitDialect = configurationValues .containsKey ( Settings .DIALECT );
47
- if ( explicitDialect ) {
48
- DialectFactory dialectFactory = registry .getService ( DialectFactory .class );
49
- return new JdbcEnvironmentImpl ( registry , dialectFactory .buildDialect ( configurationValues , null ) );
50
- }
49
+ protected void logConnectionInfo (DatabaseConnectionInfo databaseConnectionInfo ) {
50
+ // Nothing to do we log the connection info somewhere else
51
+ }
51
52
52
- return new JdbcEnvironmentImpl ( registry , new DialectBuilder ( configurationValues , registry ).build () );
53
+ @ Override
54
+ protected JdbcEnvironmentImpl getJdbcEnvironmentWithExplicitConfiguration (
55
+ Map <String , Object > configurationValues ,
56
+ ServiceRegistryImplementor registry ,
57
+ DialectFactory dialectFactory ,
58
+ DialectResolutionInfo dialectResolutionInfo ) {
59
+ return super .getJdbcEnvironmentWithExplicitConfiguration (
60
+ configurationValues ,
61
+ registry ,
62
+ dialectFactory ,
63
+ dialectResolutionInfo
64
+ );
65
+ }
66
+
67
+ @ Override
68
+ protected JdbcEnvironmentImpl getJdbcEnvironmentWithDefaults (
69
+ Map <String , Object > configurationValues ,
70
+ ServiceRegistryImplementor registry ,
71
+ DialectFactory dialectFactory ) {
72
+ return new JdbcEnvironmentImpl ( registry , new DialectBuilder ( configurationValues , registry )
73
+ .build ( dialectFactory )
74
+ );
75
+ }
76
+
77
+ @ Override
78
+ protected JdbcEnvironmentImpl getJdbcEnvironmentUsingJdbcMetadata (
79
+ Map <String , Object > configurationValues ,
80
+ ServiceRegistryImplementor registry ,
81
+ DialectFactory dialectFactory ,
82
+ String explicitDatabaseName ,
83
+ Integer explicitDatabaseMajorVersion ,
84
+ Integer explicitDatabaseMinorVersion ,
85
+ String explicitDatabaseVersion ) {
86
+ try {
87
+ final Dialect dialect = new DialectBuilder ( configurationValues , registry )
88
+ .build (
89
+ dialectFactory ,
90
+ new ExplicitMetadata (
91
+ explicitDatabaseName ,
92
+ explicitDatabaseMajorVersion ,
93
+ explicitDatabaseMinorVersion ,
94
+ explicitDatabaseVersion
95
+ )
96
+ );
97
+ return new JdbcEnvironmentImpl ( registry , dialect );
98
+ }
99
+ catch (RuntimeException e ) {
100
+ return getJdbcEnvironmentWithDefaults ( configurationValues , registry , dialectFactory );
101
+ }
53
102
}
54
103
55
104
private static class DialectBuilder {
@@ -62,24 +111,40 @@ public DialectBuilder(Map<String, Object> configurationValues, ServiceRegistry r
62
111
this .registry = registry ;
63
112
}
64
113
65
- public Dialect build () {
66
- DialectFactory dialectFactory = registry .getService ( DialectFactory .class );
114
+ public Dialect build (DialectFactory dialectFactory ) {
67
115
return dialectFactory .buildDialect ( configurationValues , this ::dialectResolutionInfo );
68
116
}
69
117
118
+ public Dialect build (DialectFactory dialectFactory , ExplicitMetadata explicitMetadata ) {
119
+ return dialectFactory .buildDialect ( configurationValues , () -> dialectResolutionInfo ( explicitMetadata ) );
120
+ }
121
+
70
122
private DialectResolutionInfo dialectResolutionInfo () {
71
- ReactiveConnectionPool connectionPool = registry .getService ( ReactiveConnectionPool .class );
72
- return connectionPool
123
+ return dialectResolutionInfo ( DialectBuilder ::buildResolutionInfo );
124
+ }
125
+
126
+ private DialectResolutionInfo dialectResolutionInfo (ExplicitMetadata explicitMetadata ) {
127
+ return dialectResolutionInfo ( reactiveConnection -> DialectBuilder
128
+ .buildResolutionInfo ( reactiveConnection , explicitMetadata )
129
+ );
130
+ }
131
+
132
+ private DialectResolutionInfo dialectResolutionInfo (Function <ReactiveConnection , CompletionStage <ReactiveDialectResolutionInfo >> dialectResolutionFunction ) {
133
+ return registry
134
+ .getService ( ReactiveConnectionPool .class )
73
135
// The default SqlExceptionHelper in ORM requires the dialect, but we haven't created a dialect yet,
74
136
// so we need to override it at this stage, or we will have an exception.
75
137
.getConnection ( new SqlExceptionHelper ( true ) )
76
- .thenCompose ( DialectBuilder :: buildResolutionInfo )
138
+ .thenCompose ( dialectResolutionFunction )
77
139
.toCompletableFuture ().join ();
78
140
}
79
141
80
142
private static CompletionStage <ReactiveDialectResolutionInfo > buildResolutionInfo (ReactiveConnection connection ) {
81
- final DatabaseMetadata databaseMetadata = connection .getDatabaseMetadata ();
82
- return resolutionInfoStage ( connection , databaseMetadata )
143
+ return buildResolutionInfo ( connection , null );
144
+ }
145
+
146
+ private static CompletionStage <ReactiveDialectResolutionInfo > buildResolutionInfo (ReactiveConnection connection , ExplicitMetadata explicitMetadata ) {
147
+ return resolutionInfoStage ( connection , explicitMetadata )
83
148
.handle ( CompletionStages ::handle )
84
149
.thenCompose ( handled -> {
85
150
if ( handled .hasFailed () ) {
@@ -96,19 +161,27 @@ private static CompletionStage<ReactiveDialectResolutionInfo> buildResolutionInf
96
161
} );
97
162
}
98
163
99
- private static CompletionStage <ReactiveDialectResolutionInfo > resolutionInfoStage (ReactiveConnection connection , DatabaseMetadata databaseMetadata ) {
100
- if ( databaseMetadata .productName ().equalsIgnoreCase ( "PostgreSQL" ) ) {
101
- // We need to check if the database is PostgreSQL or CockroachDB
102
- // Hibernate ORM does it using a query, so we need to check in advance
164
+ /**
165
+ * @see org.hibernate.dialect.Database#POSTGRESQL for recognizing CockroachDB
166
+ */
167
+ private static CompletionStage <ReactiveDialectResolutionInfo > resolutionInfoStage (ReactiveConnection connection , ExplicitMetadata explicitMetadata ) {
168
+ final DatabaseMetadata databaseMetadata = explicitMetadata != null
169
+ ? new ReactiveDatabaseMetadata ( connection .getDatabaseMetadata (), explicitMetadata )
170
+ : connection .getDatabaseMetadata ();
171
+
172
+ // If the product name is explicitly set to Postgres, we are not going to override it
173
+ if ( ( explicitMetadata == null || explicitMetadata .productName == null )
174
+ && databaseMetadata .productName ().equalsIgnoreCase ( "PostgreSQL" ) ) {
175
+ // CockroachDB returns "PostgreSQL" as product name in the metadata.
176
+ // So, we need to check if the database is PostgreSQL or CockroachDB
177
+ // We follow the same approach used by ORM: run a new query and check the full version metadata
103
178
// See org.hibernate.dialect.Database.POSTGRESQL#createDialect
104
179
return connection .select ( "select version()" )
105
180
.thenApply ( DialectBuilder ::readFullVersion )
106
- .thenApply ( fullversion -> {
107
- if ( fullversion .startsWith ( "Cockroach" ) ) {
108
- return new CockroachDatabaseMetadata ( fullversion );
109
- }
110
- return databaseMetadata ;
111
- } )
181
+ .thenApply ( fullVersion -> fullVersion .startsWith ( "Cockroach" )
182
+ ? new ReactiveDatabaseMetadata ( "Cockroach" , databaseMetadata )
183
+ : databaseMetadata
184
+ )
112
185
.thenApply ( ReactiveDialectResolutionInfo ::new );
113
186
}
114
187
@@ -122,32 +195,62 @@ private static String readFullVersion(ReactiveConnection.Result result) {
122
195
}
123
196
}
124
197
125
- private static class CockroachDatabaseMetadata implements DatabaseMetadata {
198
+ /**
199
+ * Utility class to pass around explicit metadata properties.
200
+ * It's different from {@link DatabaseMetadata} because values can be null.
201
+ */
202
+ private static class ExplicitMetadata {
203
+ private final String productName ;
204
+ private final String fullVersion ;
205
+ private final Integer majorVersion ;
206
+ private final Integer minorVersion ;
207
+
208
+ public ExplicitMetadata (String explicitDatabaseName , Integer explicitDatabaseMajorVersion , Integer explicitDatabaseMinorVersion , String explicitDatabaseVersion ) {
209
+ this .productName = explicitDatabaseName ;
210
+ this .fullVersion = explicitDatabaseVersion ;
211
+ this .majorVersion = explicitDatabaseMajorVersion ;
212
+ this .minorVersion = explicitDatabaseMinorVersion ;
213
+ }
214
+ }
126
215
127
- private final String fullversion ;
216
+ private static class ReactiveDatabaseMetadata implements DatabaseMetadata {
217
+ public final String productName ;
218
+ public final String fullVersion ;
219
+ public final int majorVersion ;
220
+ public final int minorVersion ;
221
+
222
+ public ReactiveDatabaseMetadata (String productName , DatabaseMetadata databaseMetadata ) {
223
+ this .productName = productName ;
224
+ this .fullVersion = databaseMetadata .productName ();
225
+ this .majorVersion = databaseMetadata .majorVersion ();
226
+ this .minorVersion = databaseMetadata .minorVersion ();
227
+ }
128
228
129
- public CockroachDatabaseMetadata (String fullversion ) {
130
- this .fullversion = fullversion ;
229
+ public ReactiveDatabaseMetadata (DatabaseMetadata metadata , ExplicitMetadata explicitMetadata ) {
230
+ productName = requireNonNullElse ( explicitMetadata .productName , metadata .productName () );
231
+ fullVersion = requireNonNullElse ( explicitMetadata .fullVersion , metadata .fullVersion () );
232
+ majorVersion = requireNonNullElse ( explicitMetadata .majorVersion , metadata .majorVersion () );
233
+ minorVersion = requireNonNullElse ( explicitMetadata .minorVersion , metadata .minorVersion () );
131
234
}
132
235
133
236
@ Override
134
237
public String productName () {
135
- return "CockroachDb" ;
238
+ return productName ;
136
239
}
137
240
138
241
@ Override
139
242
public String fullVersion () {
140
- return fullversion ;
243
+ return fullVersion ;
141
244
}
142
245
143
246
@ Override
144
247
public int majorVersion () {
145
- return 0 ;
248
+ return majorVersion ;
146
249
}
147
250
148
251
@ Override
149
252
public int minorVersion () {
150
- return 0 ;
253
+ return minorVersion ;
151
254
}
152
255
}
153
256
@@ -179,6 +282,14 @@ public int getDatabaseMinorVersion() {
179
282
return metadata .minorVersion ();
180
283
}
181
284
285
+ @ Override
286
+ public int getDatabaseMicroVersion () {
287
+ // Hibernate ORM extracts the micro version from the product version using
288
+ // databaseMicroVersion(DatabaseMetaData) in JdbcEnvironmentInitiator.
289
+ // But, I don't think it's used at the moment, so I will just ignore it.
290
+ return DatabaseVersion .NO_VERSION ;
291
+ }
292
+
182
293
@ Override
183
294
public String getDriverName () {
184
295
return getDatabaseName ();
@@ -196,6 +307,7 @@ public int getDriverMinorVersion() {
196
307
197
308
@ Override
198
309
public String getSQLKeywords () {
310
+ // Vert.x metadata doesn't have this info
199
311
return null ;
200
312
}
201
313
0 commit comments