77package org .hibernate .generator .internal ;
88
99import org .hibernate .AssertionFailure ;
10+ import org .hibernate .SessionFactory ;
1011import org .hibernate .annotations .CreationTimestamp ;
1112import org .hibernate .annotations .CurrentTimestamp ;
1213import org .hibernate .annotations .SourceType ;
1314import org .hibernate .annotations .UpdateTimestamp ;
1415import org .hibernate .dialect .Dialect ;
16+ import org .hibernate .engine .config .spi .ConfigurationService ;
1517import org .hibernate .engine .jdbc .Size ;
1618import org .hibernate .engine .spi .SharedSessionContractImplementor ;
1719import org .hibernate .generator .EventType ;
3638import java .time .OffsetTime ;
3739import java .time .Year ;
3840import java .time .YearMonth ;
39- import java .time .ZoneId ;
4041import java .time .ZonedDateTime ;
4142import java .util .Calendar ;
4243import java .util .Date ;
4344import java .util .EnumSet ;
4445import java .util .HashMap ;
4546import java .util .Map ;
4647import java .util .concurrent .ConcurrentHashMap ;
47- import java .util .function .IntFunction ;
48+ import java .util .function .BiFunction ;
49+
50+ import org .checkerframework .checker .nullness .qual .Nullable ;
4851
4952import static org .hibernate .generator .EventTypeSets .INSERT_AND_UPDATE ;
5053import static org .hibernate .generator .EventTypeSets .INSERT_ONLY ;
6871 * @author Gavin King
6972 */
7073public class CurrentTimestampGeneration implements BeforeExecutionGenerator , OnExecutionGenerator {
74+
75+ /**
76+ * Configuration property name to set a custom {@link Clock} for Hibernate ORM to use when generating VM based
77+ * timestamp values for e.g. {@link CurrentTimestamp}, {@link CreationTimestamp}, {@link UpdateTimestamp}
78+ * and {@link org.hibernate.type.descriptor.java.VersionJavaType} methods.
79+ *
80+ * @since 6.6
81+ */
82+ public static final String CLOCK_SETTING_NAME = "hibernate.testing.clock" ;
83+
7184 private final EnumSet <EventType > eventTypes ;
7285
7386 private final CurrentTimestampGeneratorDelegate delegate ;
74- private static final Map <Class <?>, IntFunction < CurrentTimestampGeneratorDelegate >> GENERATOR_PRODUCERS = new HashMap <>();
87+ private static final Map <Class <?>, BiFunction < @ Nullable Clock , Integer , CurrentTimestampGeneratorDelegate >> GENERATOR_PRODUCERS = new HashMap <>();
7588 private static final Map <Key , CurrentTimestampGeneratorDelegate > GENERATOR_DELEGATES = new ConcurrentHashMap <>();
7689
7790 static {
7891 GENERATOR_PRODUCERS .put (
7992 Date .class ,
80- precision -> {
81- final Clock clock = ClockHelper .forPrecision ( precision , 3 );
93+ ( baseClock , precision ) -> {
94+ final Clock clock = ClockHelper .forPrecision ( baseClock , precision , 3 );
8295 return () -> new Date ( clock .millis () );
8396 }
8497 );
8598 GENERATOR_PRODUCERS .put (
8699 Calendar .class ,
87- precision -> {
88- final Clock clock = ClockHelper .forPrecision ( precision , 3 );
100+ ( baseClock , precision ) -> {
101+ final Clock clock = ClockHelper .forPrecision ( baseClock , precision , 3 );
89102 return () -> {
90103 Calendar calendar = Calendar .getInstance ();
91104 calendar .setTimeInMillis ( clock .millis () );
@@ -95,78 +108,78 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE
95108 );
96109 GENERATOR_PRODUCERS .put (
97110 java .sql .Date .class ,
98- precision -> () -> new java .sql .Date ( System .currentTimeMillis () )
111+ ( baseClock , precision ) -> () -> new java .sql .Date ( baseClock == null ? System .currentTimeMillis () : baseClock . millis () )
99112 );
100113
101114 GENERATOR_PRODUCERS .put (
102115 Time .class ,
103- precision -> {
104- final Clock clock = ClockHelper .forPrecision ( precision , 3 );
116+ ( baseClock , precision ) -> {
117+ final Clock clock = ClockHelper .forPrecision ( baseClock , precision , 3 );
105118 return () -> new Time ( clock .millis () );
106119 }
107120 );
108121 GENERATOR_PRODUCERS .put (
109122 Timestamp .class ,
110- precision -> {
111- final Clock clock = ClockHelper .forPrecision ( precision , 9 );
123+ ( baseClock , precision ) -> {
124+ final Clock clock = ClockHelper .forPrecision ( baseClock , precision , 9 );
112125 return () -> Timestamp .from ( clock .instant () );
113126 }
114127 );
115128 GENERATOR_PRODUCERS .put (
116129 Instant .class ,
117- precision -> {
118- final Clock clock = ClockHelper .forPrecision ( precision , 9 );
130+ ( baseClock , precision ) -> {
131+ final Clock clock = ClockHelper .forPrecision ( baseClock , precision , 9 );
119132 return clock ::instant ;
120133 }
121134 );
122135 GENERATOR_PRODUCERS .put (
123136 LocalDate .class ,
124- precision -> LocalDate :: now
137+ ( baseClock , precision ) -> () -> LocalDate . now ( baseClock == null ? Clock . systemDefaultZone () : baseClock )
125138 );
126139 GENERATOR_PRODUCERS .put (
127140 LocalDateTime .class ,
128- precision -> {
129- final Clock clock = ClockHelper .forPrecision ( precision , 9 );
141+ ( baseClock , precision ) -> {
142+ final Clock clock = ClockHelper .forPrecision ( baseClock , precision , 9 );
130143 return () -> LocalDateTime .now ( clock );
131144 }
132145 );
133146 GENERATOR_PRODUCERS .put (
134147 LocalTime .class ,
135- precision -> {
136- final Clock clock = ClockHelper .forPrecision ( precision , 9 );
148+ ( baseClock , precision ) -> {
149+ final Clock clock = ClockHelper .forPrecision ( baseClock , precision , 9 );
137150 return () -> LocalTime .now ( clock );
138151 }
139152 );
140153 GENERATOR_PRODUCERS .put (
141154 MonthDay .class ,
142- precision -> MonthDay :: now
155+ ( baseClock , precision ) -> () -> MonthDay . now ( baseClock == null ? Clock . systemDefaultZone () : baseClock )
143156 );
144157 GENERATOR_PRODUCERS .put (
145158 OffsetDateTime .class ,
146- precision -> {
147- final Clock clock = ClockHelper .forPrecision ( precision , 9 );
159+ ( baseClock , precision ) -> {
160+ final Clock clock = ClockHelper .forPrecision ( baseClock , precision , 9 );
148161 return () -> OffsetDateTime .now ( clock );
149162 }
150163 );
151164 GENERATOR_PRODUCERS .put (
152165 OffsetTime .class ,
153- precision -> {
154- final Clock clock = ClockHelper .forPrecision ( precision , 9 );
166+ ( baseClock , precision ) -> {
167+ final Clock clock = ClockHelper .forPrecision ( baseClock , precision , 9 );
155168 return () -> OffsetTime .now ( clock );
156169 }
157170 );
158171 GENERATOR_PRODUCERS .put (
159172 Year .class ,
160- precision -> Year :: now
173+ ( baseClock , precision ) -> () -> Year . now ( baseClock == null ? Clock . systemDefaultZone () : baseClock )
161174 );
162175 GENERATOR_PRODUCERS .put (
163176 YearMonth .class ,
164- precision -> YearMonth :: now
177+ ( baseClock , precision ) -> () -> YearMonth . now ( baseClock == null ? Clock . systemDefaultZone () : baseClock )
165178 );
166179 GENERATOR_PRODUCERS .put (
167180 ZonedDateTime .class ,
168- precision -> {
169- final Clock clock = ClockHelper .forPrecision ( precision , 9 );
181+ ( baseClock , precision ) -> {
182+ final Clock clock = ClockHelper .forPrecision ( baseClock , precision , 9 );
170183 return () -> ZonedDateTime .now ( clock );
171184 }
172185 );
@@ -208,16 +221,19 @@ static CurrentTimestampGeneratorDelegate getGeneratorDelegate(
208221 context .getDatabase ().getDialect (),
209222 basicValue .getMetadata ()
210223 );
211- final Key key = new Key ( propertyType , size .getPrecision () == null ? 0 : size .getPrecision () );
224+ final Clock baseClock = context .getServiceRegistry ()
225+ .requireService ( ConfigurationService .class )
226+ .getSetting ( CLOCK_SETTING_NAME , value -> (Clock ) value );
227+ final Key key = new Key ( propertyType , baseClock , size .getPrecision () == null ? 0 : size .getPrecision () );
212228 final CurrentTimestampGeneratorDelegate delegate = GENERATOR_DELEGATES .get ( key );
213229 if ( delegate != null ) {
214230 return delegate ;
215231 }
216- final IntFunction < CurrentTimestampGeneratorDelegate > producer = GENERATOR_PRODUCERS .get ( key .clazz );
232+ final BiFunction < @ Nullable Clock , Integer , CurrentTimestampGeneratorDelegate > producer = GENERATOR_PRODUCERS .get ( key .clazz );
217233 if ( producer == null ) {
218234 return null ;
219235 }
220- final CurrentTimestampGeneratorDelegate generatorDelegate = producer .apply ( key .precision );
236+ final CurrentTimestampGeneratorDelegate generatorDelegate = producer .apply ( key .clock , key . precision );
221237 final CurrentTimestampGeneratorDelegate old = GENERATOR_DELEGATES .putIfAbsent (
222238 key ,
223239 generatorDelegate
@@ -230,6 +246,10 @@ static CurrentTimestampGeneratorDelegate getGeneratorDelegate(
230246 }
231247 }
232248
249+ public static <T extends Clock > T getClock (SessionFactory sessionFactory ) {
250+ return (T ) sessionFactory .getProperties ().get ( CLOCK_SETTING_NAME );
251+ }
252+
233253 @ Override
234254 public boolean generatedOnExecution () {
235255 return delegate == null ;
@@ -267,10 +287,12 @@ interface CurrentTimestampGeneratorDelegate {
267287
268288 private static class Key {
269289 private final Class <?> clazz ;
290+ private final @ Nullable Clock clock ;
270291 private final int precision ;
271292
272- public Key (Class <?> clazz , int precision ) {
293+ public Key (Class <?> clazz , @ Nullable Clock clock , int precision ) {
273294 this .clazz = clazz ;
295+ this .clock = clock ;
274296 this .precision = precision ;
275297 }
276298
@@ -288,12 +310,13 @@ public boolean equals(Object o) {
288310 if ( precision != key .precision ) {
289311 return false ;
290312 }
291- return clazz .equals ( key .clazz );
313+ return clock == key . clock && clazz .equals ( key .clazz );
292314 }
293315
294316 @ Override
295317 public int hashCode () {
296318 int result = clazz .hashCode ();
319+ result = 31 * result + ( clock == null ? 0 : clock .hashCode () );
297320 result = 31 * result + precision ;
298321 return result ;
299322 }
0 commit comments