HHH-16240 - Add test and fix for issue, and refactor TimestampGenerators out of CurrentTimestampGeneration

Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
Jan Schatteman 2023-03-01 22:19:44 +01:00 committed by Christian Beikov
parent 1ff682438d
commit 6118369a56
3 changed files with 176 additions and 41 deletions

View File

@ -19,11 +19,25 @@ import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.generator.GeneratorCreationContext;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.TimestampGenerators;
import org.hibernate.tuple.ValueGenerator;
import java.lang.reflect.Member;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.MonthDay;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import static org.hibernate.generator.EventTypeSets.INSERT_AND_UPDATE;
import static org.hibernate.generator.EventTypeSets.INSERT_ONLY;
@ -48,30 +62,100 @@ import static org.hibernate.generator.EventTypeSets.fromArray;
*/
public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnExecutionGenerator {
private final EnumSet<EventType> eventTypes;
private final ValueGenerator<?> generator;
private final CurrentTimestampGeneratorDelegate delegate;
private static final Map<Class<?>, CurrentTimestampGeneratorDelegate> generatorDelegates = new HashMap<>();
static {
generatorDelegates.put(
Date.class,
Date::new
);
generatorDelegates.put(
Calendar.class,
() -> {
Calendar calendar = Calendar.getInstance();
calendar.setTime( new Date() );
return calendar;
}
);
generatorDelegates.put(
java.sql.Date.class,
() -> new java.sql.Date( System.currentTimeMillis() )
);
generatorDelegates.put(
Time.class,
() -> new Time( System.currentTimeMillis() )
);
generatorDelegates.put(
Timestamp.class,
() -> new Timestamp( System.currentTimeMillis() )
);
generatorDelegates.put(
Instant.class,
Instant::now
);
generatorDelegates.put(
LocalDate.class,
LocalDate::now
);
generatorDelegates.put(
LocalDateTime.class,
LocalDateTime::now
);
generatorDelegates.put(
LocalTime.class,
LocalTime::now
);
generatorDelegates.put(
MonthDay.class,
MonthDay::now
);
generatorDelegates.put(
OffsetDateTime.class,
OffsetDateTime::now
);
generatorDelegates.put(
OffsetTime.class,
OffsetTime::now
);
generatorDelegates.put(
Year.class,
Year::now
);
generatorDelegates.put(
YearMonth.class,
YearMonth::now
);
generatorDelegates.put(
ZonedDateTime.class,
ZonedDateTime::now
);
}
public CurrentTimestampGeneration(CurrentTimestamp annotation, Member member, GeneratorCreationContext context) {
generator = getGenerator( annotation.source(), member );
delegate = getGeneratorDelegate( annotation.source(), member );
eventTypes = annotation.timing() == GenerationTiming.ALWAYS
? fromArray( annotation.event() )
: annotation.timing().getEquivalent().eventTypes();
}
public CurrentTimestampGeneration(CreationTimestamp annotation, Member member, GeneratorCreationContext context) {
generator = getGenerator( annotation.source(), member );
delegate = getGeneratorDelegate( annotation.source(), member );
eventTypes = INSERT_ONLY;
}
public CurrentTimestampGeneration(UpdateTimestamp annotation, Member member, GeneratorCreationContext context) {
generator = getGenerator( annotation.source(), member );
delegate = getGeneratorDelegate( annotation.source(), member );
eventTypes = INSERT_AND_UPDATE;
}
private static ValueGenerator<?> getGenerator(SourceType source, Member member) {
private static CurrentTimestampGeneratorDelegate getGeneratorDelegate(SourceType source, Member member) {
switch (source) {
case VM:
// ValueGenerator is only used for in-VM generation
return TimestampGenerators.get( ReflectHelper.getPropertyType( member ) );
// Generator is only used for in-VM generation
return generatorDelegates.get( ReflectHelper.getPropertyType( member ) );
case DB:
return null;
default:
@ -81,7 +165,7 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE
@Override
public boolean generatedOnExecution() {
return generator == null;
return delegate == null;
}
@Override
@ -91,7 +175,7 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE
@Override
public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) {
return generator.generateValue( session.asSessionImplementor(), owner, currentValue );
return delegate.generate();
}
@Override
@ -108,4 +192,9 @@ public class CurrentTimestampGeneration implements BeforeExecutionGenerator, OnE
public String[] getReferencedColumnValues(Dialect dialect) {
return new String[] { dialect.currentTimestamp() };
}
private interface CurrentTimestampGeneratorDelegate {
// Left out the Generator params, they're not used anyway. Since this is purely internal, this can be changed if needed
Object generate();
}
}

View File

@ -1,5 +1,7 @@
package org.hibernate.orm.test.annotations;
import org.hibernate.Session;
import org.hibernate.StatelessSession;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
import org.junit.Assert;
@ -15,6 +17,8 @@ import java.time.*;
import java.util.Calendar;
import java.util.Date;
import org.hibernate.testing.TestForIssue;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
@ -170,21 +174,40 @@ public class CreationTimestampTest extends BaseEntityManagerFunctionalTestCase {
Event event = new Event();
entityManager.persist(event);
entityManager.flush();
Assert.assertNotNull(event.getDate());
Assert.assertNotNull(event.getCalendar());
Assert.assertNotNull(event.getSqlDate());
Assert.assertNotNull(event.getTime());
Assert.assertNotNull(event.getTimestamp());
Assert.assertNotNull(event.getInstant());
Assert.assertNotNull(event.getLocalDate());
Assert.assertNotNull(event.getLocalDateTime());
Assert.assertNotNull(event.getLocalTime());
Assert.assertNotNull(event.getMonthDay());
Assert.assertNotNull(event.getOffsetDateTime());
Assert.assertNotNull(event.getOffsetTime());
Assert.assertNotNull(event.getYear());
Assert.assertNotNull(event.getYearMonth());
Assert.assertNotNull(event.getZonedDateTime());
check( event );
});
}
@Test
@TestForIssue( jiraKey = "HHH-16240")
public void generatesCurrentTimestampInStatelessSession() {
doInJPA(this::entityManagerFactory, entityManager -> {
Session session = entityManager.unwrap( Session.class);
try (StatelessSession statelessSession = session.getSessionFactory().openStatelessSession()) {
Event event = new Event();
statelessSession.getTransaction().begin();
statelessSession.insert(event);
statelessSession.getTransaction().commit();
check( event );
}
});
}
private void check(Event event) {
Assert.assertNotNull(event.getDate());
Assert.assertNotNull(event.getCalendar());
Assert.assertNotNull(event.getSqlDate());
Assert.assertNotNull(event.getTime());
Assert.assertNotNull(event.getTimestamp());
Assert.assertNotNull(event.getInstant());
Assert.assertNotNull(event.getLocalDate());
Assert.assertNotNull(event.getLocalDateTime());
Assert.assertNotNull(event.getLocalTime());
Assert.assertNotNull(event.getMonthDay());
Assert.assertNotNull(event.getOffsetDateTime());
Assert.assertNotNull(event.getOffsetTime());
Assert.assertNotNull(event.getYear());
Assert.assertNotNull(event.getYearMonth());
Assert.assertNotNull(event.getZonedDateTime());
}
}

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.orm.test.annotations;
import org.hibernate.Session;
import org.hibernate.StatelessSession;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
import org.junit.Assert;
@ -21,6 +23,8 @@ import java.time.*;
import java.util.Calendar;
import java.util.Date;
import org.hibernate.testing.TestForIssue;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
@ -176,21 +180,40 @@ public class UpdateTimestampTest extends BaseEntityManagerFunctionalTestCase {
Event event = new Event();
entityManager.persist(event);
entityManager.flush();
Assert.assertNotNull(event.getDate());
Assert.assertNotNull(event.getCalendar());
Assert.assertNotNull(event.getSqlDate());
Assert.assertNotNull(event.getTime());
Assert.assertNotNull(event.getTimestamp());
Assert.assertNotNull(event.getInstant());
Assert.assertNotNull(event.getLocalDate());
Assert.assertNotNull(event.getLocalDateTime());
Assert.assertNotNull(event.getLocalTime());
Assert.assertNotNull(event.getMonthDay());
Assert.assertNotNull(event.getOffsetDateTime());
Assert.assertNotNull(event.getOffsetTime());
Assert.assertNotNull(event.getYear());
Assert.assertNotNull(event.getYearMonth());
Assert.assertNotNull(event.getZonedDateTime());
check( event );
});
}
@Test
@TestForIssue( jiraKey = "HHH-16240")
public void generatesCurrentTimestampInStatelessSession() {
doInJPA(this::entityManagerFactory, entityManager -> {
Session session = entityManager.unwrap( Session.class);
try (StatelessSession statelessSession = session.getSessionFactory().openStatelessSession()) {
Event event = new Event();
statelessSession.getTransaction().begin();
statelessSession.insert(event);
statelessSession.getTransaction().commit();
check( event );
}
});
}
private void check(Event event) {
Assert.assertNotNull(event.getDate());
Assert.assertNotNull(event.getCalendar());
Assert.assertNotNull(event.getSqlDate());
Assert.assertNotNull(event.getTime());
Assert.assertNotNull(event.getTimestamp());
Assert.assertNotNull(event.getInstant());
Assert.assertNotNull(event.getLocalDate());
Assert.assertNotNull(event.getLocalDateTime());
Assert.assertNotNull(event.getLocalTime());
Assert.assertNotNull(event.getMonthDay());
Assert.assertNotNull(event.getOffsetDateTime());
Assert.assertNotNull(event.getOffsetTime());
Assert.assertNotNull(event.getYear());
Assert.assertNotNull(event.getYearMonth());
Assert.assertNotNull(event.getZonedDateTime());
}
}