HHH-18395 Fix intermittent failures of clock based tests by using custom clock
This commit is contained in:
parent
a17b241f40
commit
5b2a87c5e8
|
@ -9,9 +9,10 @@ package org.hibernate.engine.internal;
|
|||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.SharedSessionContract;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.IdentifierValue;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionDelegatorBaseImpl;
|
||||
import org.hibernate.engine.spi.VersionValue;
|
||||
import org.hibernate.mapping.KeyValue;
|
||||
|
@ -85,7 +86,8 @@ public class UnsavedValueFactory {
|
|||
Integer precision,
|
||||
Integer scale,
|
||||
Getter getter,
|
||||
Supplier<?> templateInstanceAccess) {
|
||||
Supplier<?> templateInstanceAccess,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
final String unsavedValue = bootVersionMapping.getNullValue();
|
||||
if ( unsavedValue == null ) {
|
||||
if ( getter != null && templateInstanceAccess != null ) {
|
||||
|
@ -95,8 +97,7 @@ public class UnsavedValueFactory {
|
|||
|
||||
// if the version of a newly instantiated object is not the same
|
||||
// as the version seed value, use that as the unsaved-value
|
||||
final T seedValue = jtd.seed( length, precision, scale,
|
||||
mockSession( bootVersionMapping.getBuildingContext() ) );
|
||||
final T seedValue = jtd.seed( length, precision, scale, mockSession( sessionFactory ) );
|
||||
return jtd.areEqual( seedValue, defaultValue )
|
||||
? VersionValue.UNDEFINED
|
||||
: new VersionValue( defaultValue );
|
||||
|
@ -121,12 +122,27 @@ public class UnsavedValueFactory {
|
|||
|
||||
}
|
||||
|
||||
private static SharedSessionDelegatorBaseImpl mockSession(MetadataBuildingContext context) {
|
||||
private static SharedSessionDelegatorBaseImpl mockSession(SessionFactoryImplementor sessionFactory) {
|
||||
return new SharedSessionDelegatorBaseImpl(null) {
|
||||
|
||||
@Override
|
||||
protected SharedSessionContract delegate() {
|
||||
throw new UnsupportedOperationException( "Operation not supported" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionFactoryImplementor getFactory() {
|
||||
return sessionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionFactoryImplementor getSessionFactory() {
|
||||
return sessionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcServices getJdbcServices() {
|
||||
return context.getBootstrapContext().getServiceRegistry()
|
||||
.requireService( JdbcServices.class );
|
||||
return sessionFactory.getJdbcServices();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
package org.hibernate.generator.internal;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.CurrentTimestamp;
|
||||
import org.hibernate.annotations.SourceType;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.engine.jdbc.Size;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.EventType;
|
||||
|
@ -36,7 +38,6 @@ import java.time.OffsetDateTime;
|
|||
import java.time.OffsetTime;
|
||||
import java.time.Year;
|
||||
import java.time.YearMonth;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
@ -44,7 +45,9 @@ import java.util.EnumSet;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import static org.hibernate.generator.EventTypeSets.INSERT_AND_UPDATE;
|
||||
import static org.hibernate.generator.EventTypeSets.INSERT_ONLY;
|
||||
|
@ -68,24 +71,34 @@ import static org.hibernate.generator.EventTypeSets.fromArray;
|
|||
* @author Gavin King
|
||||
*/
|
||||
public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnExecutionGenerator {
|
||||
|
||||
/**
|
||||
* Configuration property name to set a custom {@link Clock} for Hibernate ORM to use when generating VM based
|
||||
* timestamp values for e.g. {@link CurrentTimestamp}, {@link CreationTimestamp}, {@link UpdateTimestamp}
|
||||
* and {@link org.hibernate.type.descriptor.java.VersionJavaType} methods.
|
||||
*
|
||||
* @since 6.6
|
||||
*/
|
||||
public static final String CLOCK_SETTING_NAME = "hibernate.testing.clock";
|
||||
|
||||
private final EnumSet<EventType> eventTypes;
|
||||
|
||||
private final CurrentTimestampGeneratorDelegate delegate;
|
||||
private static final Map<Class<?>, IntFunction<CurrentTimestampGeneratorDelegate>> GENERATOR_PRODUCERS = new HashMap<>();
|
||||
private static final Map<Class<?>, BiFunction<@Nullable Clock, Integer, CurrentTimestampGeneratorDelegate>> GENERATOR_PRODUCERS = new HashMap<>();
|
||||
private static final Map<Key, CurrentTimestampGeneratorDelegate> GENERATOR_DELEGATES = new ConcurrentHashMap<>();
|
||||
|
||||
static {
|
||||
GENERATOR_PRODUCERS.put(
|
||||
Date.class,
|
||||
precision -> {
|
||||
final Clock clock = ClockHelper.forPrecision( precision, 3 );
|
||||
(baseClock, precision) -> {
|
||||
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 3 );
|
||||
return () -> new Date( clock.millis() );
|
||||
}
|
||||
);
|
||||
GENERATOR_PRODUCERS.put(
|
||||
Calendar.class,
|
||||
precision -> {
|
||||
final Clock clock = ClockHelper.forPrecision( precision, 3 );
|
||||
(baseClock, precision) -> {
|
||||
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 3 );
|
||||
return () -> {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTimeInMillis( clock.millis() );
|
||||
|
@ -95,78 +108,78 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE
|
|||
);
|
||||
GENERATOR_PRODUCERS.put(
|
||||
java.sql.Date.class,
|
||||
precision -> () -> new java.sql.Date( System.currentTimeMillis() )
|
||||
(baseClock, precision) -> () -> new java.sql.Date( baseClock == null ? System.currentTimeMillis() : baseClock.millis() )
|
||||
);
|
||||
|
||||
GENERATOR_PRODUCERS.put(
|
||||
Time.class,
|
||||
precision -> {
|
||||
final Clock clock = ClockHelper.forPrecision( precision, 3 );
|
||||
(baseClock, precision) -> {
|
||||
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 3 );
|
||||
return () -> new Time( clock.millis() );
|
||||
}
|
||||
);
|
||||
GENERATOR_PRODUCERS.put(
|
||||
Timestamp.class,
|
||||
precision -> {
|
||||
final Clock clock = ClockHelper.forPrecision( precision, 9 );
|
||||
(baseClock, precision) -> {
|
||||
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 9 );
|
||||
return () -> Timestamp.from( clock.instant() );
|
||||
}
|
||||
);
|
||||
GENERATOR_PRODUCERS.put(
|
||||
Instant.class,
|
||||
precision -> {
|
||||
final Clock clock = ClockHelper.forPrecision( precision, 9 );
|
||||
(baseClock, precision) -> {
|
||||
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 9 );
|
||||
return clock::instant;
|
||||
}
|
||||
);
|
||||
GENERATOR_PRODUCERS.put(
|
||||
LocalDate.class,
|
||||
precision -> LocalDate::now
|
||||
(baseClock, precision) -> () -> LocalDate.now( baseClock == null ? Clock.systemDefaultZone() : baseClock )
|
||||
);
|
||||
GENERATOR_PRODUCERS.put(
|
||||
LocalDateTime.class,
|
||||
precision -> {
|
||||
final Clock clock = ClockHelper.forPrecision( precision, 9 );
|
||||
(baseClock, precision) -> {
|
||||
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 9 );
|
||||
return () -> LocalDateTime.now( clock );
|
||||
}
|
||||
);
|
||||
GENERATOR_PRODUCERS.put(
|
||||
LocalTime.class,
|
||||
precision -> {
|
||||
final Clock clock = ClockHelper.forPrecision( precision, 9 );
|
||||
(baseClock, precision) -> {
|
||||
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 9 );
|
||||
return () -> LocalTime.now( clock );
|
||||
}
|
||||
);
|
||||
GENERATOR_PRODUCERS.put(
|
||||
MonthDay.class,
|
||||
precision -> MonthDay::now
|
||||
(baseClock, precision) -> () -> MonthDay.now( baseClock == null ? Clock.systemDefaultZone() : baseClock )
|
||||
);
|
||||
GENERATOR_PRODUCERS.put(
|
||||
OffsetDateTime.class,
|
||||
precision -> {
|
||||
final Clock clock = ClockHelper.forPrecision( precision, 9 );
|
||||
(baseClock, precision) -> {
|
||||
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 9 );
|
||||
return () -> OffsetDateTime.now( clock );
|
||||
}
|
||||
);
|
||||
GENERATOR_PRODUCERS.put(
|
||||
OffsetTime.class,
|
||||
precision -> {
|
||||
final Clock clock = ClockHelper.forPrecision( precision, 9 );
|
||||
(baseClock, precision) -> {
|
||||
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 9 );
|
||||
return () -> OffsetTime.now( clock );
|
||||
}
|
||||
);
|
||||
GENERATOR_PRODUCERS.put(
|
||||
Year.class,
|
||||
precision -> Year::now
|
||||
(baseClock, precision) -> () -> Year.now( baseClock == null ? Clock.systemDefaultZone() : baseClock )
|
||||
);
|
||||
GENERATOR_PRODUCERS.put(
|
||||
YearMonth.class,
|
||||
precision -> YearMonth::now
|
||||
(baseClock, precision) -> () -> YearMonth.now( baseClock == null ? Clock.systemDefaultZone() : baseClock )
|
||||
);
|
||||
GENERATOR_PRODUCERS.put(
|
||||
ZonedDateTime.class,
|
||||
precision -> {
|
||||
final Clock clock = ClockHelper.forPrecision( precision, 9 );
|
||||
(baseClock, precision) -> {
|
||||
final Clock clock = ClockHelper.forPrecision( baseClock, precision, 9 );
|
||||
return () -> ZonedDateTime.now( clock );
|
||||
}
|
||||
);
|
||||
|
@ -208,16 +221,19 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE
|
|||
context.getDatabase().getDialect(),
|
||||
basicValue.getMetadata()
|
||||
);
|
||||
final Key key = new Key( propertyType, size.getPrecision() == null ? 0 : size.getPrecision() );
|
||||
final Clock baseClock = context.getServiceRegistry()
|
||||
.requireService( ConfigurationService.class )
|
||||
.getSetting( CLOCK_SETTING_NAME, value -> (Clock) value );
|
||||
final Key key = new Key( propertyType, baseClock, size.getPrecision() == null ? 0 : size.getPrecision() );
|
||||
final CurrentTimestampGeneratorDelegate delegate = GENERATOR_DELEGATES.get( key );
|
||||
if ( delegate != null ) {
|
||||
return delegate;
|
||||
}
|
||||
final IntFunction<CurrentTimestampGeneratorDelegate> producer = GENERATOR_PRODUCERS.get( key.clazz );
|
||||
final BiFunction<@Nullable Clock, Integer, CurrentTimestampGeneratorDelegate> producer = GENERATOR_PRODUCERS.get( key.clazz );
|
||||
if ( producer == null ) {
|
||||
return null;
|
||||
}
|
||||
final CurrentTimestampGeneratorDelegate generatorDelegate = producer.apply( key.precision );
|
||||
final CurrentTimestampGeneratorDelegate generatorDelegate = producer.apply( key.clock, key.precision );
|
||||
final CurrentTimestampGeneratorDelegate old = GENERATOR_DELEGATES.putIfAbsent(
|
||||
key,
|
||||
generatorDelegate
|
||||
|
@ -230,6 +246,10 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE
|
|||
}
|
||||
}
|
||||
|
||||
public static <T extends Clock> T getClock(SessionFactory sessionFactory) {
|
||||
return (T) sessionFactory.getProperties().get( CLOCK_SETTING_NAME );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean generatedOnExecution() {
|
||||
return delegate == null;
|
||||
|
@ -267,10 +287,12 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE
|
|||
|
||||
private static class Key {
|
||||
private final Class<?> clazz;
|
||||
private final @Nullable Clock clock;
|
||||
private final int precision;
|
||||
|
||||
public Key(Class<?> clazz, int precision) {
|
||||
public Key(Class<?> clazz, @Nullable Clock clock, int precision) {
|
||||
this.clazz = clazz;
|
||||
this.clock = clock;
|
||||
this.precision = precision;
|
||||
}
|
||||
|
||||
|
@ -288,12 +310,13 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE
|
|||
if ( precision != key.precision ) {
|
||||
return false;
|
||||
}
|
||||
return clazz.equals( key.clazz );
|
||||
return clock == key.clock && clazz.equals( key.clazz );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = clazz.hashCode();
|
||||
result = 31 * result + ( clock == null ? 0 : clock.hashCode() );
|
||||
result = 31 * result + precision;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -97,7 +97,8 @@ public class EntityVersionMappingImpl implements EntityVersionMapping, FetchOpti
|
|||
.getRepresentationStrategy()
|
||||
.resolvePropertyAccess( bootEntityDescriptor.getVersion() )
|
||||
.getGetter(),
|
||||
templateInstanceAccess
|
||||
templateInstanceAccess,
|
||||
creationProcess.getCreationContext().getSessionFactory()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,9 @@ import java.time.Clock;
|
|||
import java.time.Duration;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.internal.CurrentTimestampGeneration;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* Helper for determining the correct clock for precision
|
||||
|
@ -17,15 +20,15 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|||
public class ClockHelper {
|
||||
|
||||
private static final Clock TICK_9 = Clock.systemDefaultZone();
|
||||
private static final Clock TICK_8 = Clock.tick( TICK_9, Duration.ofNanos( 10L ) );
|
||||
private static final Clock TICK_7 = Clock.tick( TICK_9, Duration.ofNanos( 100L ) );
|
||||
private static final Clock TICK_6 = Clock.tick( TICK_9, Duration.ofNanos( 1000L ) );
|
||||
private static final Clock TICK_5 = Clock.tick( TICK_9, Duration.ofNanos( 10000L ) );
|
||||
private static final Clock TICK_4 = Clock.tick( TICK_9, Duration.ofNanos( 100000L ) );
|
||||
private static final Clock TICK_3 = Clock.tick( TICK_9, Duration.ofNanos( 1000000L ) );
|
||||
private static final Clock TICK_2 = Clock.tick( TICK_9, Duration.ofNanos( 10000000L ) );
|
||||
private static final Clock TICK_1 = Clock.tick( TICK_9, Duration.ofNanos( 100000000L ) );
|
||||
private static final Clock TICK_0 = Clock.tick( TICK_9, Duration.ofNanos( 1000000000L ) );
|
||||
private static final Clock TICK_8 = forPrecision( TICK_9, 8, 9 );
|
||||
private static final Clock TICK_7 = forPrecision( TICK_9, 7, 9 );
|
||||
private static final Clock TICK_6 = forPrecision( TICK_9, 6, 9 );
|
||||
private static final Clock TICK_5 = forPrecision( TICK_9, 5, 9 );
|
||||
private static final Clock TICK_4 = forPrecision( TICK_9, 4, 9 );
|
||||
private static final Clock TICK_3 = forPrecision( TICK_9, 3, 9 );
|
||||
private static final Clock TICK_2 = forPrecision( TICK_9, 2, 9 );
|
||||
private static final Clock TICK_1 = forPrecision( TICK_9, 1, 9 );
|
||||
private static final Clock TICK_0 = forPrecision( TICK_9, 0, 9 );
|
||||
|
||||
public static Clock forPrecision(Integer precision, SharedSessionContractImplementor session) {
|
||||
return forPrecision( precision, session, 9 );
|
||||
|
@ -39,31 +42,38 @@ public class ClockHelper {
|
|||
else {
|
||||
resolvedPrecision = precision;
|
||||
}
|
||||
return forPrecision( resolvedPrecision, maxPrecision );
|
||||
final Clock baseClock = (Clock) session.getFactory()
|
||||
.getProperties()
|
||||
.get( CurrentTimestampGeneration.CLOCK_SETTING_NAME );
|
||||
return forPrecision( baseClock, resolvedPrecision, maxPrecision );
|
||||
}
|
||||
|
||||
public static Clock forPrecision(int resolvedPrecision, int maxPrecision) {
|
||||
return forPrecision( null, resolvedPrecision, maxPrecision );
|
||||
}
|
||||
|
||||
public static Clock forPrecision(@Nullable Clock baseClock, int resolvedPrecision, int maxPrecision) {
|
||||
switch ( Math.min( resolvedPrecision, maxPrecision ) ) {
|
||||
case 0:
|
||||
return TICK_0;
|
||||
return baseClock == null ? TICK_0 : Clock.tick( baseClock, Duration.ofNanos( 1000000000L ) );
|
||||
case 1:
|
||||
return TICK_1;
|
||||
return baseClock == null ? TICK_1 : Clock.tick( baseClock, Duration.ofNanos( 100000000L ) );
|
||||
case 2:
|
||||
return TICK_2;
|
||||
return baseClock == null ? TICK_2 : Clock.tick( baseClock, Duration.ofNanos( 10000000L ) );
|
||||
case 3:
|
||||
return TICK_3;
|
||||
return baseClock == null ? TICK_3 : Clock.tick( baseClock, Duration.ofNanos( 1000000L ) );
|
||||
case 4:
|
||||
return TICK_4;
|
||||
return baseClock == null ? TICK_4 : Clock.tick( baseClock, Duration.ofNanos( 100000L ) );
|
||||
case 5:
|
||||
return TICK_5;
|
||||
return baseClock == null ? TICK_5 : Clock.tick( baseClock, Duration.ofNanos( 10000L ) );
|
||||
case 6:
|
||||
return TICK_6;
|
||||
return baseClock == null ? TICK_6 : Clock.tick( baseClock, Duration.ofNanos( 1000L ) );
|
||||
case 7:
|
||||
return TICK_7;
|
||||
return baseClock == null ? TICK_7 : Clock.tick( baseClock, Duration.ofNanos( 100L ) );
|
||||
case 8:
|
||||
return TICK_8;
|
||||
return baseClock == null ? TICK_8 : Clock.tick( baseClock, Duration.ofNanos( 10L ) );
|
||||
case 9:
|
||||
return TICK_9;
|
||||
return baseClock == null ? TICK_9 : baseClock;
|
||||
}
|
||||
throw new IllegalArgumentException( "Illegal precision: " + resolvedPrecision );
|
||||
}
|
||||
|
|
|
@ -7,15 +7,17 @@
|
|||
package org.hibernate.orm.test.annotations;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
import org.hibernate.generator.internal.CurrentTimestampGeneration;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.JiraKey;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -25,9 +27,11 @@ import static org.junit.Assert.assertTrue;
|
|||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-13256")
|
||||
@JiraKey("HHH-13256")
|
||||
public class InMemoryUpdateTimestampTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
private static final MutableClock clock = new MutableClock();
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
|
@ -35,6 +39,12 @@ public class InMemoryUpdateTimestampTest extends BaseEntityManagerFunctionalTest
|
|||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConfigOptions(Map options) {
|
||||
super.addConfigOptions( options );
|
||||
options.put( CurrentTimestampGeneration.CLOCK_SETTING_NAME, clock );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
|
@ -47,6 +57,7 @@ public class InMemoryUpdateTimestampTest extends BaseEntityManagerFunctionalTest
|
|||
entityManager.flush();
|
||||
Assert.assertNotNull( person.getUpdatedOn() );
|
||||
} );
|
||||
clock.tick();
|
||||
|
||||
AtomicReference<Date> beforeTimestamp = new AtomicReference<>();
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.orm.test.annotations;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
public final class MutableClock extends Clock {
|
||||
|
||||
private static final Instant START_INSTANT = LocalDateTime.of( 2000, 1, 1, 12, 30, 50, 123456789 )
|
||||
.toInstant( ZoneOffset.UTC );
|
||||
|
||||
private final ZoneId zoneId;
|
||||
private Instant instant;
|
||||
|
||||
public MutableClock() {
|
||||
this( ZoneId.systemDefault(), START_INSTANT );
|
||||
}
|
||||
|
||||
public MutableClock(ZoneId zoneId) {
|
||||
this( zoneId, START_INSTANT );
|
||||
}
|
||||
|
||||
public MutableClock(Instant instant) {
|
||||
this( ZoneId.systemDefault(), instant );
|
||||
}
|
||||
|
||||
public MutableClock(ZoneId zoneId, Instant instant) {
|
||||
this.zoneId = zoneId;
|
||||
this.instant = instant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZoneId getZone() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clock withZone(ZoneId zone) {
|
||||
return new MutableClock( zone, instant );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant instant() {
|
||||
return instant;
|
||||
}
|
||||
|
||||
public void setInstant(Instant instant) {
|
||||
this.instant = instant;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
instant = START_INSTANT;
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
instant = instant.plusSeconds( 1 );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.orm.test.annotations;
|
||||
|
||||
import java.time.Clock;
|
||||
|
||||
import org.hibernate.testing.orm.junit.SettingProvider;
|
||||
|
||||
public final class MutableClockSettingProvider implements SettingProvider.Provider<Clock> {
|
||||
|
||||
@Override
|
||||
public Clock getSetting() {
|
||||
return new MutableClock();
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ package org.hibernate.orm.test.annotations;
|
|||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
|
@ -25,9 +26,10 @@ import jakarta.persistence.TemporalType;
|
|||
import org.hibernate.Session;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
import org.hibernate.generator.internal.CurrentTimestampGeneration;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.JiraKey;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -41,26 +43,32 @@ import static org.junit.Assert.assertTrue;
|
|||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-11867")
|
||||
@JiraKey("HHH-11867")
|
||||
public class UpdateTimeStampInheritanceTest extends BaseEntityManagerFunctionalTestCase {
|
||||
private static final long SLEEP_MILLIS = 25;
|
||||
|
||||
private static final String customerId = "1";
|
||||
private static final MutableClock clock = new MutableClock();
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { Customer.class, AbstractPerson.class, Address.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConfigOptions(Map options) {
|
||||
super.addConfigOptions( options );
|
||||
options.put( CurrentTimestampGeneration.CLOCK_SETTING_NAME, clock );
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
clock.reset();
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Customer customer = new Customer();
|
||||
customer.setId( customerId );
|
||||
customer.addAddress( "address" );
|
||||
entityManager.persist( customer );
|
||||
} );
|
||||
sleep( SLEEP_MILLIS );
|
||||
clock.tick();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -72,6 +80,7 @@ public class UpdateTimeStampInheritanceTest extends BaseEntityManagerFunctionalT
|
|||
assertModifiedAtWasNotUpdated( customer );
|
||||
customer.setName( "xyz" );
|
||||
} );
|
||||
clock.tick();
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Customer customer = entityManager.find( Customer.class, customerId );
|
||||
|
@ -90,6 +99,7 @@ public class UpdateTimeStampInheritanceTest extends BaseEntityManagerFunctionalT
|
|||
assertModifiedAtWasNotUpdated( customer );
|
||||
customer.setEmail( "xyz@" );
|
||||
} );
|
||||
clock.tick();
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Customer customer = entityManager.find( Customer.class, customerId );
|
||||
|
@ -110,6 +120,7 @@ public class UpdateTimeStampInheritanceTest extends BaseEntityManagerFunctionalT
|
|||
a.setStreet( "Lollard street" );
|
||||
customer.setWorkAddress( a );
|
||||
} );
|
||||
clock.tick();
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Customer customer = entityManager.find( Customer.class, customerId );
|
||||
|
@ -130,6 +141,7 @@ public class UpdateTimeStampInheritanceTest extends BaseEntityManagerFunctionalT
|
|||
a.setStreet( "Lollard Street" );
|
||||
customer.setHomeAddress( a );
|
||||
} );
|
||||
clock.tick();
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Customer customer = entityManager.find( Customer.class, customerId );
|
||||
|
@ -150,6 +162,7 @@ public class UpdateTimeStampInheritanceTest extends BaseEntityManagerFunctionalT
|
|||
adresses.add( "another address" );
|
||||
customer.setAdresses( adresses );
|
||||
} );
|
||||
clock.tick();
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Customer customer = entityManager.find( Customer.class, customerId );
|
||||
|
@ -169,6 +182,7 @@ public class UpdateTimeStampInheritanceTest extends BaseEntityManagerFunctionalT
|
|||
Set<String> books = new HashSet<>();
|
||||
customer.setBooks( books );
|
||||
} );
|
||||
clock.tick();
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Customer customer = entityManager.find( Customer.class, customerId );
|
||||
|
|
|
@ -25,7 +25,6 @@ import jakarta.persistence.Entity;
|
|||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import org.hibernate.HibernateError;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
@ -36,16 +35,23 @@ import org.hibernate.annotations.UpdateTimestamp;
|
|||
import org.hibernate.dialect.MySQLDialect;
|
||||
import org.hibernate.dialect.SybaseDialect;
|
||||
import org.hibernate.dialect.TiDBDialect;
|
||||
import org.hibernate.generator.internal.CurrentTimestampGeneration;
|
||||
import org.hibernate.orm.test.annotations.MutableClock;
|
||||
import org.hibernate.orm.test.annotations.MutableClockSettingProvider;
|
||||
import org.hibernate.tuple.ValueGenerator;
|
||||
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.JiraKey;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.SettingProvider;
|
||||
import org.hibernate.testing.orm.junit.SkipForDialect;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -65,9 +71,19 @@ import static org.junit.Assert.assertTrue;
|
|||
@SkipForDialect( dialectClass = TiDBDialect.class, reason = "See HHH-10196" )
|
||||
@DomainModel( annotatedClasses = DefaultGeneratedValueTest.TheEntity.class )
|
||||
@SessionFactory
|
||||
@ServiceRegistry(settingProviders = @SettingProvider(settingName = CurrentTimestampGeneration.CLOCK_SETTING_NAME, provider = MutableClockSettingProvider.class))
|
||||
public class DefaultGeneratedValueTest {
|
||||
|
||||
private MutableClock clock;
|
||||
|
||||
@BeforeEach
|
||||
public void setup(SessionFactoryScope scope) {
|
||||
clock = CurrentTimestampGeneration.getClock( scope.getSessionFactory() );
|
||||
clock.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-2907" )
|
||||
@JiraKey( "HHH-2907" )
|
||||
public void testGeneration(SessionFactoryScope scope) {
|
||||
final TheEntity created = scope.fromTransaction( (s) -> {
|
||||
final TheEntity theEntity = new TheEntity( 1 );
|
||||
|
@ -153,8 +169,7 @@ public class DefaultGeneratedValueTest {
|
|||
assertNotNull( created.vmCreatedSqlTimestamp );
|
||||
assertNotNull( created.updated );
|
||||
|
||||
//We need to wait a little to make sure the timestamps produced are different
|
||||
waitALittle();
|
||||
clock.tick();
|
||||
|
||||
scope.inTransaction( (s) -> {
|
||||
final TheEntity theEntity = s.get( TheEntity.class, 1 );
|
||||
|
@ -260,12 +275,4 @@ public class DefaultGeneratedValueTest {
|
|||
}
|
||||
}
|
||||
|
||||
private static void waitALittle() {
|
||||
try {
|
||||
Thread.sleep( 10 );
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new HibernateError( "Unexpected wakeup from test sleep" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.orm.test.mapping.generated;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import jakarta.persistence.Basic;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
|
@ -14,11 +15,17 @@ import jakarta.persistence.Table;
|
|||
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
import org.hibernate.generator.internal.CurrentTimestampGeneration;
|
||||
import org.hibernate.orm.test.annotations.MutableClock;
|
||||
import org.hibernate.orm.test.annotations.MutableClockSettingProvider;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.SettingProvider;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -31,14 +38,24 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
*/
|
||||
@DomainModel( annotatedClasses = InVmGenerationsWithAnnotationsTests.AuditedEntity.class )
|
||||
@SessionFactory
|
||||
@ServiceRegistry(settingProviders = @SettingProvider(settingName = CurrentTimestampGeneration.CLOCK_SETTING_NAME, provider = MutableClockSettingProvider.class))
|
||||
public class InVmGenerationsWithAnnotationsTests {
|
||||
|
||||
private MutableClock clock;
|
||||
|
||||
@BeforeEach
|
||||
public void setup(SessionFactoryScope scope) {
|
||||
clock = CurrentTimestampGeneration.getClock( scope.getSessionFactory() );
|
||||
clock.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerations(SessionFactoryScope scope) {
|
||||
scope.inSession( (session) -> {
|
||||
// first creation
|
||||
final AuditedEntity saved = scope.fromTransaction( session, (s) -> {
|
||||
final AuditedEntity saved = scope.fromTransaction( session, s -> {
|
||||
final AuditedEntity entity = new AuditedEntity( 1, "it" );
|
||||
session.persist( entity );
|
||||
s.persist( entity );
|
||||
return entity;
|
||||
} );
|
||||
|
||||
|
@ -47,18 +64,10 @@ public class InVmGenerationsWithAnnotationsTests {
|
|||
assertThat( saved.lastUpdatedOn ).isNotNull();
|
||||
|
||||
saved.name = "changed";
|
||||
// Let's sleep a millisecond to make sure we actually generate a different timestamp
|
||||
try {
|
||||
Thread.sleep( 1L );
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// Ignore
|
||||
}
|
||||
clock.tick();
|
||||
|
||||
// then changing
|
||||
final AuditedEntity merged = scope.fromTransaction( session, (s) -> {
|
||||
return (AuditedEntity) session.merge( saved );
|
||||
} );
|
||||
final AuditedEntity merged = scope.fromTransaction( session, s -> s.merge( saved ) );
|
||||
|
||||
assertThat( merged ).isNotNull();
|
||||
assertThat( merged.createdOn ).isNotNull();
|
||||
|
@ -66,9 +75,7 @@ public class InVmGenerationsWithAnnotationsTests {
|
|||
assertThat( merged.lastUpdatedOn ).isNotEqualTo( merged.createdOn );
|
||||
|
||||
// lastly, make sure we can load it..
|
||||
final AuditedEntity loaded = scope.fromTransaction( session, (s) -> {
|
||||
return session.get( AuditedEntity.class, 1 );
|
||||
} );
|
||||
final AuditedEntity loaded = scope.fromTransaction( session, s -> s.get( AuditedEntity.class, 1 ) );
|
||||
|
||||
assertThat( loaded ).isNotNull();
|
||||
assertThat( loaded.createdOn ).isEqualTo( merged.createdOn );
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.orm.test.mapping.generated;
|
|||
|
||||
import java.sql.Date;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import jakarta.persistence.Basic;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
|
@ -15,11 +16,17 @@ import jakarta.persistence.Table;
|
|||
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
import org.hibernate.generator.internal.CurrentTimestampGeneration;
|
||||
import org.hibernate.orm.test.annotations.MutableClock;
|
||||
import org.hibernate.orm.test.annotations.MutableClockSettingProvider;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.SettingProvider;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -32,14 +39,24 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
*/
|
||||
@DomainModel( annotatedClasses = InVmGenerationsWithAnnotationsWithMixedSqlTypesTests.AuditedEntity.class )
|
||||
@SessionFactory
|
||||
@ServiceRegistry(settingProviders = @SettingProvider(settingName = CurrentTimestampGeneration.CLOCK_SETTING_NAME, provider = MutableClockSettingProvider.class))
|
||||
public class InVmGenerationsWithAnnotationsWithMixedSqlTypesTests {
|
||||
|
||||
private MutableClock clock;
|
||||
|
||||
@BeforeEach
|
||||
public void setup(SessionFactoryScope scope) {
|
||||
clock = CurrentTimestampGeneration.getClock( scope.getSessionFactory() );
|
||||
clock.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerations(SessionFactoryScope scope) {
|
||||
scope.inSession( (session) -> {
|
||||
// first creation
|
||||
final AuditedEntity saved = scope.fromTransaction( session, (s) -> {
|
||||
final AuditedEntity saved = scope.fromTransaction( session, s -> {
|
||||
final AuditedEntity entity = new AuditedEntity( 1, "it" );
|
||||
session.persist( entity );
|
||||
s.persist( entity );
|
||||
return entity;
|
||||
} );
|
||||
|
||||
|
@ -48,18 +65,10 @@ public class InVmGenerationsWithAnnotationsWithMixedSqlTypesTests {
|
|||
assertThat( saved.lastUpdatedOn ).isNotNull();
|
||||
|
||||
saved.name = "changed";
|
||||
// Let's sleep a millisecond to make sure we actually generate a different timestamp
|
||||
try {
|
||||
Thread.sleep( 1L );
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// Ignore
|
||||
}
|
||||
clock.tick();
|
||||
|
||||
// then changing
|
||||
final AuditedEntity merged = scope.fromTransaction( session, (s) -> {
|
||||
return (AuditedEntity) session.merge( saved );
|
||||
} );
|
||||
final AuditedEntity merged = scope.fromTransaction( session, s -> s.merge( saved ) );
|
||||
|
||||
assertThat( merged ).isNotNull();
|
||||
assertThat( merged.createdOn ).isNotNull();
|
||||
|
@ -67,9 +76,7 @@ public class InVmGenerationsWithAnnotationsWithMixedSqlTypesTests {
|
|||
assertThat( merged.lastUpdatedOn ).isNotEqualTo( merged.createdOn );
|
||||
|
||||
// lastly, make sure we can load it..
|
||||
final AuditedEntity loaded = scope.fromTransaction( session, (s) -> {
|
||||
return session.get( AuditedEntity.class, 1 );
|
||||
} );
|
||||
final AuditedEntity loaded = scope.fromTransaction( session, s -> s.get( AuditedEntity.class, 1 ) );
|
||||
|
||||
assertThat( loaded ).isNotNull();
|
||||
assertThat( loaded.createdOn ).isEqualTo( merged.createdOn );
|
||||
|
|
|
@ -7,19 +7,25 @@
|
|||
package org.hibernate.orm.test.mapping.generated;
|
||||
|
||||
import java.sql.Date;
|
||||
|
||||
import jakarta.persistence.Basic;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import org.hibernate.HibernateError;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
import org.hibernate.generator.internal.CurrentTimestampGeneration;
|
||||
import org.hibernate.orm.test.annotations.MutableClock;
|
||||
import org.hibernate.orm.test.annotations.MutableClockSettingProvider;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.SettingProvider;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -32,14 +38,24 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
*/
|
||||
@DomainModel( annotatedClasses = InVmGenerationsWithAnnotationsWithSqlDateTests.AuditedEntity.class )
|
||||
@SessionFactory
|
||||
@ServiceRegistry(settingProviders = @SettingProvider(settingName = CurrentTimestampGeneration.CLOCK_SETTING_NAME, provider = MutableClockSettingProvider.class))
|
||||
public class InVmGenerationsWithAnnotationsWithSqlDateTests {
|
||||
|
||||
private MutableClock clock;
|
||||
|
||||
@BeforeEach
|
||||
public void setup(SessionFactoryScope scope) {
|
||||
clock = CurrentTimestampGeneration.getClock( scope.getSessionFactory() );
|
||||
clock.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerations(SessionFactoryScope scope) {
|
||||
scope.inSession( (session) -> {
|
||||
// first creation
|
||||
final AuditedEntity saved = scope.fromTransaction( session, (s) -> {
|
||||
final AuditedEntity saved = scope.fromTransaction( session, s -> {
|
||||
final AuditedEntity entity = new AuditedEntity( 1, "it" );
|
||||
session.persist( entity );
|
||||
s.persist( entity );
|
||||
return entity;
|
||||
} );
|
||||
|
||||
|
@ -48,14 +64,10 @@ public class InVmGenerationsWithAnnotationsWithSqlDateTests {
|
|||
assertThat( saved.lastUpdatedOn ).isNotNull();
|
||||
|
||||
saved.name = "changed";
|
||||
|
||||
//We need to wait a little to make sure the timestamps produced are different
|
||||
waitALittle();
|
||||
clock.tick();
|
||||
|
||||
// then changing
|
||||
final AuditedEntity merged = scope.fromTransaction( session, (s) -> {
|
||||
return (AuditedEntity) session.merge( saved );
|
||||
} );
|
||||
final AuditedEntity merged = scope.fromTransaction( session, s -> s.merge( saved ) );
|
||||
|
||||
assertThat( merged ).isNotNull();
|
||||
assertThat( merged.createdOn ).isNotNull();
|
||||
|
@ -63,9 +75,7 @@ public class InVmGenerationsWithAnnotationsWithSqlDateTests {
|
|||
assertThat( merged.lastUpdatedOn ).isNotEqualTo( merged.createdOn );
|
||||
|
||||
// lastly, make sure we can load it..
|
||||
final AuditedEntity loaded = scope.fromTransaction( session, (s) -> {
|
||||
return session.get( AuditedEntity.class, 1 );
|
||||
} );
|
||||
final AuditedEntity loaded = scope.fromTransaction( session, s -> s.get( AuditedEntity.class, 1 ) );
|
||||
|
||||
assertThat( loaded ).isNotNull();
|
||||
assertThat( loaded.createdOn ).isEqualTo( merged.createdOn );
|
||||
|
@ -98,13 +108,4 @@ public class InVmGenerationsWithAnnotationsWithSqlDateTests {
|
|||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
private static void waitALittle() {
|
||||
try {
|
||||
Thread.sleep( 10 );
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new HibernateError( "Unexpected wakeup from test sleep" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,14 +14,19 @@ import jakarta.persistence.Entity;
|
|||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import org.hibernate.HibernateError;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
import org.hibernate.generator.internal.CurrentTimestampGeneration;
|
||||
import org.hibernate.orm.test.annotations.MutableClock;
|
||||
import org.hibernate.orm.test.annotations.MutableClockSettingProvider;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.SettingProvider;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -34,14 +39,24 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
*/
|
||||
@DomainModel( annotatedClasses = InVmGenerationsWithMultipleAnnotationsTests.AuditedEntity.class )
|
||||
@SessionFactory
|
||||
@ServiceRegistry(settingProviders = @SettingProvider(settingName = CurrentTimestampGeneration.CLOCK_SETTING_NAME, provider = MutableClockSettingProvider.class))
|
||||
public class InVmGenerationsWithMultipleAnnotationsTests {
|
||||
|
||||
private MutableClock clock;
|
||||
|
||||
@BeforeEach
|
||||
public void setup(SessionFactoryScope scope) {
|
||||
clock = CurrentTimestampGeneration.getClock( scope.getSessionFactory() );
|
||||
clock.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerations(SessionFactoryScope scope) {
|
||||
scope.inSession( (session) -> {
|
||||
// first creation
|
||||
final AuditedEntity saved = scope.fromTransaction( session, (s) -> {
|
||||
final AuditedEntity saved = scope.fromTransaction( session, s -> {
|
||||
final AuditedEntity entity = new AuditedEntity( 1, "it" );
|
||||
session.persist( entity );
|
||||
s.persist( entity );
|
||||
return entity;
|
||||
} );
|
||||
|
||||
|
@ -50,14 +65,10 @@ public class InVmGenerationsWithMultipleAnnotationsTests {
|
|||
assertThat( saved.lastUpdatedOn ).isNotNull();
|
||||
|
||||
saved.name = "changed";
|
||||
|
||||
//We need to wait a little to make sure the timestamps produced are different
|
||||
waitALittle();
|
||||
clock.tick();
|
||||
|
||||
// then changing
|
||||
final AuditedEntity merged = scope.fromTransaction( session, (s) -> {
|
||||
return (AuditedEntity) session.merge( saved );
|
||||
} );
|
||||
final AuditedEntity merged = scope.fromTransaction( session, s -> s.merge( saved ) );
|
||||
|
||||
assertThat( merged ).isNotNull();
|
||||
assertThat( merged.createdOn ).isNotNull();
|
||||
|
@ -65,9 +76,7 @@ public class InVmGenerationsWithMultipleAnnotationsTests {
|
|||
assertThat( merged.lastUpdatedOn ).isNotEqualTo( merged.createdOn );
|
||||
|
||||
// lastly, make sure we can load it..
|
||||
final AuditedEntity loaded = scope.fromTransaction( session, (s) -> {
|
||||
return session.get( AuditedEntity.class, 1 );
|
||||
} );
|
||||
final AuditedEntity loaded = scope.fromTransaction( session, s -> s.get( AuditedEntity.class, 1 ) );
|
||||
|
||||
assertThat( loaded ).isNotNull();
|
||||
assertThat( loaded.createdOn ).isEqualTo( merged.createdOn );
|
||||
|
@ -110,13 +119,4 @@ public class InVmGenerationsWithMultipleAnnotationsTests {
|
|||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
private static void waitALittle() {
|
||||
try {
|
||||
Thread.sleep( 10 );
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new HibernateError( "Unexpected wakeup from test sleep" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.orm.test.tenantid;
|
||||
|
||||
import org.hibernate.HibernateError;
|
||||
import org.hibernate.PropertyValueException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.StatelessSession;
|
||||
|
@ -24,13 +23,18 @@ import org.hibernate.testing.orm.junit.SessionFactoryProducer;
|
|||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.hibernate.binder.internal.TenantIdBinder;
|
||||
import org.hibernate.generator.internal.CurrentTimestampGeneration;
|
||||
import org.hibernate.orm.test.annotations.MutableClock;
|
||||
import org.hibernate.orm.test.annotations.MutableClockSettingProvider;
|
||||
import org.hibernate.query.Query;
|
||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||
import org.hibernate.query.criteria.JpaCriteriaQuery;
|
||||
import org.hibernate.query.criteria.JpaRoot;
|
||||
|
||||
import org.hibernate.testing.orm.junit.SettingProvider;
|
||||
import org.hibernate.testing.orm.junit.SkipForDialect;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
@ -54,11 +58,19 @@ import java.util.List;
|
|||
@ServiceRegistry(
|
||||
settings = {
|
||||
@Setting(name = JAKARTA_HBM2DDL_DATABASE_ACTION, value = "create-drop")
|
||||
}
|
||||
},
|
||||
settingProviders = @SettingProvider(settingName = CurrentTimestampGeneration.CLOCK_SETTING_NAME, provider = MutableClockSettingProvider.class)
|
||||
)
|
||||
public class TenantIdTest implements SessionFactoryProducer {
|
||||
|
||||
String currentTenant;
|
||||
MutableClock clock;
|
||||
|
||||
@BeforeEach
|
||||
public void setup(SessionFactoryScope scope) {
|
||||
clock = CurrentTimestampGeneration.getClock( scope.getSessionFactory() );
|
||||
clock.reset();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanup(SessionFactoryScope scope) {
|
||||
|
@ -211,8 +223,7 @@ public class TenantIdTest implements SessionFactoryProducer {
|
|||
assertEquals( "mine", record.state.tenantId );
|
||||
assertNotNull( record.state.updated );
|
||||
|
||||
//We need to wait a little to make sure the timestamps produced are different
|
||||
waitALittle();
|
||||
clock.tick();
|
||||
|
||||
scope.inTransaction( s -> {
|
||||
Record r = s.find( Record.class, record.id );
|
||||
|
@ -342,13 +353,4 @@ public class TenantIdTest implements SessionFactoryProducer {
|
|||
JpaRoot<Record> from = criteriaQuery.from( Record.class );
|
||||
return session.createQuery( criteriaQuery ).getResultList();
|
||||
}
|
||||
|
||||
private static void waitALittle() {
|
||||
try {
|
||||
Thread.sleep( 10 );
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new HibernateError( "Unexpected wakeup from test sleep" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue