5
5
package org .hibernate .generator .internal ;
6
6
7
7
import java .lang .reflect .Member ;
8
+ import java .sql .CallableStatement ;
9
+ import java .sql .PreparedStatement ;
10
+ import java .sql .ResultSet ;
11
+ import java .sql .SQLException ;
8
12
import java .sql .Time ;
9
13
import java .sql .Timestamp ;
10
14
import java .time .Clock ;
26
30
import java .util .concurrent .ConcurrentHashMap ;
27
31
import java .util .function .BiFunction ;
28
32
29
- import org .hibernate .AssertionFailure ;
30
33
import org .hibernate .SessionFactory ;
31
34
import org .hibernate .annotations .CreationTimestamp ;
32
35
import org .hibernate .annotations .CurrentTimestamp ;
35
38
import org .hibernate .dialect .Dialect ;
36
39
import org .hibernate .engine .config .spi .ConfigurationService ;
37
40
import org .hibernate .engine .jdbc .Size ;
41
+ import org .hibernate .engine .jdbc .spi .JdbcCoordinator ;
42
+ import org .hibernate .engine .jdbc .spi .StatementPreparer ;
38
43
import org .hibernate .engine .spi .SharedSessionContractImplementor ;
39
44
import org .hibernate .generator .BeforeExecutionGenerator ;
40
45
import org .hibernate .generator .EventType ;
45
50
import org .hibernate .type .descriptor .java .ClockHelper ;
46
51
47
52
import org .checkerframework .checker .nullness .qual .Nullable ;
53
+ import org .hibernate .type .descriptor .java .JavaType ;
48
54
55
+ import static java .sql .Types .TIMESTAMP ;
56
+ import static org .hibernate .engine .jdbc .JdbcLogging .JDBC_MESSAGE_LOGGER ;
49
57
import static org .hibernate .generator .EventTypeSets .INSERT_AND_UPDATE ;
50
58
import static org .hibernate .generator .EventTypeSets .INSERT_ONLY ;
51
59
import static org .hibernate .generator .EventTypeSets .fromArray ;
@@ -80,6 +88,8 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE
80
88
81
89
private final EnumSet <EventType > eventTypes ;
82
90
91
+ private final JavaType <Object > propertyType ;
92
+
83
93
private final CurrentTimestampGeneratorDelegate delegate ;
84
94
private static final Map <Class <?>, BiFunction <@ Nullable Clock , Integer , CurrentTimestampGeneratorDelegate >> GENERATOR_PRODUCERS = new HashMap <>();
85
95
private static final Map <Key , CurrentTimestampGeneratorDelegate > GENERATOR_DELEGATES = new ConcurrentHashMap <>();
@@ -182,19 +192,27 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE
182
192
);
183
193
}
184
194
195
+ private static JavaType <Object > getPropertyType (GeneratorCreationContext context ) {
196
+ return context .getDatabase ().getTypeConfiguration ().getJavaTypeRegistry ()
197
+ .getDescriptor ( context .getProperty ().getType ().getReturnedClass () );
198
+ }
199
+
185
200
public CurrentTimestampGeneration (CurrentTimestamp annotation , Member member , GeneratorCreationContext context ) {
186
201
delegate = getGeneratorDelegate ( annotation .source (), member , context );
187
202
eventTypes = fromArray ( annotation .event () );
203
+ propertyType = getPropertyType ( context );
188
204
}
189
205
190
206
public CurrentTimestampGeneration (CreationTimestamp annotation , Member member , GeneratorCreationContext context ) {
191
207
delegate = getGeneratorDelegate ( annotation .source (), member , context );
192
208
eventTypes = INSERT_ONLY ;
209
+ propertyType = getPropertyType ( context );
193
210
}
194
211
195
212
public CurrentTimestampGeneration (UpdateTimestamp annotation , Member member , GeneratorCreationContext context ) {
196
213
delegate = getGeneratorDelegate ( annotation .source (), member , context );
197
214
eventTypes = INSERT_AND_UPDATE ;
215
+ propertyType = getPropertyType ( context );
198
216
}
199
217
200
218
private static CurrentTimestampGeneratorDelegate getGeneratorDelegate (
@@ -208,36 +226,42 @@ static CurrentTimestampGeneratorDelegate getGeneratorDelegate(
208
226
SourceType source ,
209
227
Class <?> propertyType ,
210
228
GeneratorCreationContext context ) {
211
- switch (source ) {
212
- case VM :
229
+ return switch (source ) {
230
+ case DB -> null ;
231
+ case VM -> {
213
232
// Generator is only used for in-VM generation
214
- final BasicValue basicValue = (BasicValue ) context .getProperty ().getValue ();
215
- final Size size = basicValue .getColumns ().get ( 0 ).getColumnSize (
216
- context .getDatabase ().getDialect (),
217
- basicValue .getMetadata ()
218
- );
219
- final Clock baseClock =
220
- context .getServiceRegistry ().requireService ( ConfigurationService .class )
221
- .getSetting ( CLOCK_SETTING_NAME , value -> (Clock ) value );
222
- final Key key = new Key ( propertyType , baseClock , size .getPrecision () == null ? 0 : size .getPrecision () );
223
- final CurrentTimestampGeneratorDelegate delegate = GENERATOR_DELEGATES .get ( key );
233
+ final Key key = new Key ( propertyType , getBaseClock ( context ), getPrecision ( context ) );
234
+ final var delegate = GENERATOR_DELEGATES .get ( key );
224
235
if ( delegate != null ) {
225
- return delegate ;
226
- }
227
- final var producer = GENERATOR_PRODUCERS .get ( key .clazz );
228
- if ( producer == null ) {
229
- return null ;
236
+ yield delegate ;
230
237
}
231
238
else {
232
- final var generatorDelegate = producer .apply ( key .clock , key .precision );
233
- final var old = GENERATOR_DELEGATES .putIfAbsent ( key , generatorDelegate );
234
- return old != null ? old : generatorDelegate ;
239
+ final var producer = GENERATOR_PRODUCERS .get ( key .clazz );
240
+ if ( producer == null ) {
241
+ yield null ;
242
+ }
243
+ else {
244
+ final var generatorDelegate = producer .apply ( key .clock , key .precision );
245
+ final var old = GENERATOR_DELEGATES .putIfAbsent ( key , generatorDelegate );
246
+ yield old != null ? old : generatorDelegate ;
247
+ }
235
248
}
236
- case DB :
237
- return null ;
238
- default :
239
- throw new AssertionFailure ("unknown source" );
240
- }
249
+ }
250
+ };
251
+ }
252
+
253
+ private static int getPrecision (GeneratorCreationContext context ) {
254
+ final BasicValue basicValue = (BasicValue ) context .getProperty ().getValue ();
255
+ final Size size =
256
+ basicValue .getColumns ().get ( 0 )
257
+ .getColumnSize ( context .getDatabase ().getDialect (),
258
+ basicValue .getMetadata () );
259
+ return size .getPrecision () == null ? 0 : size .getPrecision ();
260
+ }
261
+
262
+ private static Clock getBaseClock (GeneratorCreationContext context ) {
263
+ return context .getServiceRegistry ().requireService ( ConfigurationService .class )
264
+ .getSetting ( CLOCK_SETTING_NAME , value -> (Clock ) value );
241
265
}
242
266
243
267
public static <T extends Clock > T getClock (SessionFactory sessionFactory ) {
@@ -256,7 +280,15 @@ public EnumSet<EventType> getEventTypes() {
256
280
257
281
@ Override
258
282
public Object generate (SharedSessionContractImplementor session , Object owner , Object currentValue , EventType eventType ) {
259
- return delegate .generate ();
283
+ if ( delegate == null ) {
284
+ if ( eventType != EventType .FORCE_INCREMENT ) {
285
+ throw new UnsupportedOperationException ( "CurrentTimestampGeneration.generate() should not have been called" );
286
+ }
287
+ return propertyType .wrap ( getCurrentTimestamp ( session ), session );
288
+ }
289
+ else {
290
+ return delegate .generate ();
291
+ }
260
292
}
261
293
262
294
@ Override
@@ -281,4 +313,51 @@ interface CurrentTimestampGeneratorDelegate {
281
313
282
314
private record Key (Class <?> clazz , @ Nullable Clock clock , int precision ) {
283
315
}
316
+
317
+ static Timestamp getCurrentTimestamp (SharedSessionContractImplementor session ) {
318
+ final Dialect dialect = session .getJdbcServices ().getJdbcEnvironment ().getDialect ();
319
+ final boolean callable = dialect .isCurrentTimestampSelectStringCallable ();
320
+ final String timestampSelectString = dialect .getCurrentTimestampSelectString ();
321
+ final JdbcCoordinator coordinator = session .getJdbcCoordinator ();
322
+ final StatementPreparer statementPreparer = coordinator .getStatementPreparer ();
323
+ PreparedStatement statement = null ;
324
+ try {
325
+ statement = statementPreparer .prepareStatement ( timestampSelectString , callable );
326
+ final Timestamp ts = callable
327
+ ? extractCalledResult ( statement , coordinator , timestampSelectString )
328
+ : extractResult ( statement , coordinator , timestampSelectString );
329
+ if ( JDBC_MESSAGE_LOGGER .isTraceEnabled () ) {
330
+ JDBC_MESSAGE_LOGGER .currentTimestampRetrievedFromDatabase ( ts , ts .getNanos (), ts .getTime () );
331
+ }
332
+ return ts ;
333
+ }
334
+ catch (SQLException e ) {
335
+ throw session .getJdbcServices ().getSqlExceptionHelper ().convert (
336
+ e ,
337
+ "could not obtain current timestamp from database" ,
338
+ timestampSelectString
339
+ );
340
+ }
341
+ finally {
342
+ if ( statement != null ) {
343
+ coordinator .getLogicalConnection ().getResourceRegistry ().release ( statement );
344
+ coordinator .afterStatementExecution ();
345
+ }
346
+ }
347
+ }
348
+
349
+ static Timestamp extractResult (PreparedStatement statement , JdbcCoordinator coordinator , String sql )
350
+ throws SQLException {
351
+ final ResultSet resultSet = coordinator .getResultSetReturn ().extract ( statement , sql );
352
+ resultSet .next ();
353
+ return resultSet .getTimestamp ( 1 );
354
+ }
355
+
356
+ static Timestamp extractCalledResult (PreparedStatement statement , JdbcCoordinator coordinator , String sql )
357
+ throws SQLException {
358
+ final CallableStatement callable = (CallableStatement ) statement ;
359
+ callable .registerOutParameter ( 1 , TIMESTAMP );
360
+ coordinator .getResultSetReturn ().execute ( callable , sql );
361
+ return callable .getTimestamp ( 1 );
362
+ }
284
363
}
0 commit comments