55package org .hibernate .generator .internal ;
66
77import java .lang .reflect .Member ;
8+ import java .sql .CallableStatement ;
9+ import java .sql .PreparedStatement ;
10+ import java .sql .ResultSet ;
11+ import java .sql .SQLException ;
812import java .sql .Time ;
913import java .sql .Timestamp ;
1014import java .time .Clock ;
2630import java .util .concurrent .ConcurrentHashMap ;
2731import java .util .function .BiFunction ;
2832
29- import org .hibernate .AssertionFailure ;
3033import org .hibernate .SessionFactory ;
3134import org .hibernate .annotations .CreationTimestamp ;
3235import org .hibernate .annotations .CurrentTimestamp ;
3538import org .hibernate .dialect .Dialect ;
3639import org .hibernate .engine .config .spi .ConfigurationService ;
3740import org .hibernate .engine .jdbc .Size ;
41+ import org .hibernate .engine .jdbc .spi .JdbcCoordinator ;
42+ import org .hibernate .engine .jdbc .spi .StatementPreparer ;
3843import org .hibernate .engine .spi .SharedSessionContractImplementor ;
3944import org .hibernate .generator .BeforeExecutionGenerator ;
4045import org .hibernate .generator .EventType ;
4550import org .hibernate .type .descriptor .java .ClockHelper ;
4651
4752import org .checkerframework .checker .nullness .qual .Nullable ;
53+ import org .hibernate .type .descriptor .java .JavaType ;
4854
55+ import static java .sql .Types .TIMESTAMP ;
56+ import static org .hibernate .engine .jdbc .JdbcLogging .JDBC_MESSAGE_LOGGER ;
4957import static org .hibernate .generator .EventTypeSets .INSERT_AND_UPDATE ;
5058import static org .hibernate .generator .EventTypeSets .INSERT_ONLY ;
5159import static org .hibernate .generator .EventTypeSets .fromArray ;
@@ -80,6 +88,8 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE
8088
8189 private final EnumSet <EventType > eventTypes ;
8290
91+ private final JavaType <Object > propertyType ;
92+
8393 private final CurrentTimestampGeneratorDelegate delegate ;
8494 private static final Map <Class <?>, BiFunction <@ Nullable Clock , Integer , CurrentTimestampGeneratorDelegate >> GENERATOR_PRODUCERS = new HashMap <>();
8595 private static final Map <Key , CurrentTimestampGeneratorDelegate > GENERATOR_DELEGATES = new ConcurrentHashMap <>();
@@ -182,19 +192,27 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE
182192 );
183193 }
184194
195+ private static JavaType <Object > getPropertyType (GeneratorCreationContext context ) {
196+ return context .getDatabase ().getTypeConfiguration ().getJavaTypeRegistry ()
197+ .getDescriptor ( context .getProperty ().getType ().getReturnedClass () );
198+ }
199+
185200 public CurrentTimestampGeneration (CurrentTimestamp annotation , Member member , GeneratorCreationContext context ) {
186201 delegate = getGeneratorDelegate ( annotation .source (), member , context );
187202 eventTypes = fromArray ( annotation .event () );
203+ propertyType = getPropertyType ( context );
188204 }
189205
190206 public CurrentTimestampGeneration (CreationTimestamp annotation , Member member , GeneratorCreationContext context ) {
191207 delegate = getGeneratorDelegate ( annotation .source (), member , context );
192208 eventTypes = INSERT_ONLY ;
209+ propertyType = getPropertyType ( context );
193210 }
194211
195212 public CurrentTimestampGeneration (UpdateTimestamp annotation , Member member , GeneratorCreationContext context ) {
196213 delegate = getGeneratorDelegate ( annotation .source (), member , context );
197214 eventTypes = INSERT_AND_UPDATE ;
215+ propertyType = getPropertyType ( context );
198216 }
199217
200218 private static CurrentTimestampGeneratorDelegate getGeneratorDelegate (
@@ -208,36 +226,42 @@ static CurrentTimestampGeneratorDelegate getGeneratorDelegate(
208226 SourceType source ,
209227 Class <?> propertyType ,
210228 GeneratorCreationContext context ) {
211- switch (source ) {
212- case VM :
229+ return switch (source ) {
230+ case DB -> null ;
231+ case VM -> {
213232 // 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 );
224235 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 ;
230237 }
231238 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+ }
235248 }
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 );
241265 }
242266
243267 public static <T extends Clock > T getClock (SessionFactory sessionFactory ) {
@@ -256,7 +280,15 @@ public EnumSet<EventType> getEventTypes() {
256280
257281 @ Override
258282 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+ }
260292 }
261293
262294 @ Override
@@ -281,4 +313,51 @@ interface CurrentTimestampGeneratorDelegate {
281313
282314 private record Key (Class <?> clazz , @ Nullable Clock clock , int precision ) {
283315 }
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+ }
284363}
0 commit comments