HHH-10496 Allow RevisionTimestamp to be java.time.LocalDateTime

This commit is contained in:
Chris Cranford 2021-11-25 22:09:22 -05:00
parent 267a1cdb46
commit 1abf044f2e
12 changed files with 452 additions and 50 deletions

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.envers;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
@ -174,6 +175,23 @@ public interface AuditReader {
Number getRevisionNumberForDate(Date date) throws IllegalStateException, RevisionDoesNotExistException,
IllegalArgumentException;
/**
* Gets the revision number, that corresponds to the given date. More precisely, returns
* the number of the highest revision, which was created on or before the given date. So:
* <code>getRevisionDate(getRevisionNumberForDate(date)) <= date</code> and
* <code>getRevisionDate(getRevisionNumberForDate(date)+1) > date</code>.
*
* @param date Date for which to get the revision.
*
* @return Revision number corresponding to the given date.
*
* @throws IllegalStateException If the associated entity manager is closed.
* @throws RevisionDoesNotExistException If the given date is before the first revision.
* @throws IllegalArgumentException If <code>date</code> is <code>null</code>.
*/
Number getRevisionNumberForDate(LocalDateTime date) throws IllegalStateException,
RevisionDoesNotExistException, IllegalArgumentException;
/**
* A helper method; should be used only if a custom revision entity is used. See also {@link RevisionEntity}.
*

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.envers.configuration.internal;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Locale;
import java.util.Set;
@ -36,14 +37,17 @@ import org.hibernate.envers.enhanced.OrderedSequenceGenerator;
import org.hibernate.envers.enhanced.SequenceIdRevisionEntity;
import org.hibernate.envers.enhanced.SequenceIdTrackingModifiedEntitiesRevisionEntity;
import org.hibernate.envers.internal.entities.PropertyData;
import org.hibernate.envers.internal.entities.RevisionTimestampData;
import org.hibernate.envers.internal.revisioninfo.DefaultRevisionInfoGenerator;
import org.hibernate.envers.internal.revisioninfo.DefaultTrackingModifiedEntitiesRevisionInfoGenerator;
import org.hibernate.envers.internal.revisioninfo.ModifiedEntityNamesReader;
import org.hibernate.envers.internal.revisioninfo.RevisionInfoGenerator;
import org.hibernate.envers.internal.revisioninfo.RevisionInfoNumberReader;
import org.hibernate.envers.internal.revisioninfo.RevisionInfoQueryCreator;
import org.hibernate.envers.internal.revisioninfo.RevisionTimestampValueResolver;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.service.ServiceRegistry;
/**
* @author Adam Warski (adam at warski dot org)
@ -103,8 +107,7 @@ public class RevisionInfoConfiguration {
revisionInfoQueryCreator = new RevisionInfoQueryCreator(
resolver.revisionInfoEntityName,
resolver.revisionInfoIdData.getName(),
resolver.revisionInfoTimestampData.getName(),
isTimestampAsDate( revisionInfoTimestampTypeName )
resolver.timestampValueResolver
);
if ( configuration.isTrackEntitiesChanged() ) {
@ -230,14 +233,27 @@ public class RevisionInfoConfiguration {
return mapping;
}
private boolean isTimestampAsDate(String typeName) {
return "date".equals( typeName ) || "time".equals( typeName ) || "timestamp".equals( typeName );
}
private org.hibernate.envers.boot.model.Column createColumn(String name, String type) {
return new org.hibernate.envers.boot.model.Column( name, null, null, null, type, null, null );
}
private RevisionTimestampValueResolver createRevisionTimestampResolver(
Class<?> revisionInfoClass,
PropertyData revisionInfoTimestampData,
String typeName,
ServiceRegistry serviceRegistry) {
return new RevisionTimestampValueResolver(
revisionInfoClass,
new RevisionTimestampData(
revisionInfoTimestampData.getName(),
revisionInfoTimestampData.getBeanName(),
revisionInfoTimestampData.getAccessType(),
typeName
),
serviceRegistry
);
}
private class RevisionEntityResolver {
private final MetadataImplementor metadata;
@ -258,6 +274,7 @@ public class RevisionInfoConfiguration {
private String revisionInfoTimestampTypeName;
private String revisionPropType;
private String revisionPropSqlType;
private RevisionTimestampValueResolver timestampValueResolver;
public RevisionEntityResolver(MetadataImplementor metadata, ReflectionManager reflectionManager) {
this.metadata = metadata;
@ -328,7 +345,12 @@ public class RevisionInfoConfiguration {
final Property timestampProperty = persistentClass.getProperty( revisionInfoTimestampData.getName() );
revisionInfoTimestampTypeName = timestampProperty.getType().getName();
boolean timestampAsDate = isTimestampAsDate( revisionInfoTimestampTypeName );
timestampValueResolver = createRevisionTimestampResolver(
revisionInfoClass,
revisionInfoTimestampData,
revisionInfoTimestampTypeName,
metadata.getMetadataBuildingOptions().getServiceRegistry()
);
if ( useEntityTrackingRevisionEntity( revisionInfoClass ) ) {
// If tracking modified entities is enabled, custom revision info entity is a subtype
@ -337,8 +359,7 @@ public class RevisionInfoConfiguration {
revisionInfoEntityName,
revisionInfoClass,
revisionListenerClass,
revisionInfoTimestampData,
timestampAsDate,
timestampValueResolver,
modifiedEntityNamesData,
metadata.getMetadataBuildingOptions().getServiceRegistry()
);
@ -349,8 +370,7 @@ public class RevisionInfoConfiguration {
revisionInfoEntityName,
revisionInfoClass,
revisionListenerClass,
revisionInfoTimestampData,
timestampAsDate,
timestampValueResolver,
metadata.getMetadataBuildingOptions().getServiceRegistry()
);
}
@ -359,33 +379,42 @@ public class RevisionInfoConfiguration {
if ( revisionInfoGenerator == null ) {
revisionListenerClass = getRevisionListenerClass( RevisionListener.class );
boolean timestampAsDate = isTimestampAsDate( revisionInfoTimestampTypeName );
if ( configuration.isTrackEntitiesChanged() ) {
revisionInfoClass = configuration.isNativeIdEnabled()
? DefaultTrackingModifiedEntitiesRevisionEntity.class
: SequenceIdTrackingModifiedEntitiesRevisionEntity.class;
revisionInfoEntityName = revisionInfoClass.getName();
revisionInfoGenerator = new DefaultTrackingModifiedEntitiesRevisionInfoGenerator(
revisionInfoEntityName,
revisionInfoClass,
revisionListenerClass,
revisionInfoTimestampData,
timestampAsDate,
modifiedEntityNamesData,
metadata.getMetadataBuildingOptions().getServiceRegistry()
);
}
else {
revisionInfoClass = configuration.isNativeIdEnabled()
? DefaultRevisionEntity.class
: SequenceIdRevisionEntity.class;
}
timestampValueResolver = createRevisionTimestampResolver(
revisionInfoClass,
revisionInfoTimestampData,
revisionInfoTimestampTypeName,
metadata.getMetadataBuildingOptions().getServiceRegistry()
);
if ( configuration.isTrackEntitiesChanged() ) {
revisionInfoGenerator = new DefaultTrackingModifiedEntitiesRevisionInfoGenerator(
revisionInfoEntityName,
revisionInfoClass,
revisionListenerClass,
timestampValueResolver,
modifiedEntityNamesData,
metadata.getMetadataBuildingOptions().getServiceRegistry()
);
}
else {
revisionInfoGenerator = new DefaultRevisionInfoGenerator(
revisionInfoEntityName,
revisionInfoClass,
revisionListenerClass,
revisionInfoTimestampData,
timestampAsDate,
timestampValueResolver,
metadata.getMetadataBuildingOptions().getServiceRegistry()
);
}
@ -464,12 +493,12 @@ public class RevisionInfoConfiguration {
}
final XClass propertyType = property.getType();
if ( isAnyType( propertyType, Long.class, Long.TYPE, Date.class, java.sql.Date.class ) ) {
if ( isAnyType( propertyType, Long.class, Long.TYPE, Date.class, LocalDateTime.class, java.sql.Date.class ) ) {
revisionInfoTimestampData = createPropertyData( property, accessType );
revisionTimestampFound = true;
}
else {
throwUnexpectedAnnotatedType( property, RevisionTimestamp.class, "long, Long, Date, or java.sql.Date" );
throwUnexpectedAnnotatedType( property, RevisionTimestamp.class, "long, Long, Date, LocalDateTime, or java.sql.Date" );
}
}

View File

@ -6,27 +6,39 @@
*/
package org.hibernate.envers.exception;
import java.time.LocalDateTime;
import java.util.Date;
/**
* @author Adam Warski (adam at warski dot org)
* @author Chris Cranford
*/
public class RevisionDoesNotExistException extends AuditException {
private static final long serialVersionUID = -6417768274074962282L;
private final Number revision;
private final Date date;
private final LocalDateTime localDateTime;
public RevisionDoesNotExistException(Number revision) {
super( "Revision " + revision + " does not exist." );
this.revision = revision;
this.date = null;
this.localDateTime = null;
}
public RevisionDoesNotExistException(Date date) {
super( "There is no revision before or at " + date + "." );
this.date = date;
this.revision = null;
this.localDateTime = null;
}
public RevisionDoesNotExistException(LocalDateTime localDateTime) {
super( "There is no revision before or at " + localDateTime + "." );
this.localDateTime = localDateTime;
this.revision = null;
this.date = null;
}
public Number getRevision() {
@ -36,4 +48,8 @@ public class RevisionDoesNotExistException extends AuditException {
public Date getDate() {
return date;
}
public LocalDateTime getLocalDateTime() {
return localDateTime;
}
}

View File

@ -60,7 +60,7 @@ public class PropertyData {
this.accessType = accessType;
}
private PropertyData(String name, String beanName, String accessType, Type propertyType) {
public PropertyData(String name, String beanName, String accessType, Type propertyType) {
this( name, beanName, accessType );
this.propertyType = propertyType;
}

View File

@ -0,0 +1,65 @@
/*
* 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.envers.internal.entities;
import java.util.Objects;
import org.hibernate.type.Type;
/**
* @author Chris Cranford
* @author 6.0
*/
public class RevisionTimestampData extends PropertyData {
private final String typeName;
public RevisionTimestampData(String name, String beanName, String accessType, String typeName) {
super( name, beanName, accessType );
this.typeName = typeName;
}
public RevisionTimestampData(RevisionTimestampData old, String typeName) {
this( old.getName(), old.getBeanName(), old.getAccessType(), typeName );
}
public String getTypeName() {
return typeName;
}
public boolean isTimestampDate() {
return "date".equals( typeName )
|| "time".equals( typeName )
|| "timestamp".equals( typeName );
}
public boolean isTimestampLocalDateTime() {
return "LocalDateTime".equals( typeName );
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + ( typeName != null ? typeName.hashCode() : 0 );
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
RevisionTimestampData that = (RevisionTimestampData) o;
return Objects.equals( typeName, that.typeName );
}
}

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.envers.internal.reader;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@ -204,6 +205,26 @@ public class AuditReaderImpl implements AuditReaderImplementor {
}
}
@Override
public Number getRevisionNumberForDate(LocalDateTime date) {
checkNotNull( date, "Date of revision" );
checkSession();
final Query<?> query = enversService.getRevisionInfoQueryCreator().getRevisionNumberForDateQuery( session, date );
try {
final Number res = (Number) query.uniqueResult();
if ( res == null ) {
throw new RevisionDoesNotExistException( date );
}
return res;
}
catch (NonUniqueResultException e) {
throw new AuditException( e );
}
}
@Override
@SuppressWarnings({"unchecked"})
public <T> T findRevision(Class<T> revisionEntityClass, Number revision)

View File

@ -7,18 +7,14 @@
package org.hibernate.envers.internal.revisioninfo;
import java.lang.reflect.Constructor;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.envers.EntityTrackingRevisionListener;
import org.hibernate.envers.RevisionListener;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.exception.AuditException;
import org.hibernate.envers.internal.entities.PropertyData;
import org.hibernate.envers.internal.synchronization.SessionCacheCleaner;
import org.hibernate.envers.internal.tools.ReflectionTools;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.service.ServiceRegistry;
@ -31,10 +27,9 @@ import org.hibernate.service.ServiceRegistry;
public class DefaultRevisionInfoGenerator implements RevisionInfoGenerator {
private final String revisionInfoEntityName;
private final ManagedBean<? extends RevisionListener> listenerManagedBean;
private final Setter revisionTimestampSetter;
private final boolean timestampAsDate;
private final Constructor<?> revisionInfoClassConstructor;
private final SessionCacheCleaner sessionCacheCleaner;
private final RevisionTimestampValueResolver timestampValueResolver;
private RevisionInfoNumberReader revisionInfoNumberReader;
@ -42,14 +37,12 @@ public class DefaultRevisionInfoGenerator implements RevisionInfoGenerator {
String revisionInfoEntityName,
Class<?> revisionInfoClass,
Class<? extends RevisionListener> listenerClass,
PropertyData revisionInfoTimestampData,
boolean timestampAsDate,
RevisionTimestampValueResolver timestampValueResolver,
ServiceRegistry serviceRegistry) {
this.revisionInfoEntityName = revisionInfoEntityName;
this.timestampAsDate = timestampAsDate;
this.timestampValueResolver = timestampValueResolver;
this.revisionInfoClassConstructor = ReflectHelper.getDefaultConstructor( revisionInfoClass );
this.revisionTimestampSetter = ReflectionTools.getSetter( revisionInfoClass, revisionInfoTimestampData, serviceRegistry );
this.listenerManagedBean = resolveRevisionListenerBean( listenerClass, serviceRegistry );
@ -80,8 +73,7 @@ public class DefaultRevisionInfoGenerator implements RevisionInfoGenerator {
throw new RuntimeException( e );
}
final long timestamp = System.currentTimeMillis();
revisionTimestampSetter.set( revisionInfo, timestampAsDate ? new Date( timestamp ) : timestamp, null );
timestampValueResolver.resolveNow( revisionInfo );
if ( listenerManagedBean != null ) {
listenerManagedBean.getBeanInstance().newRevision( revisionInfo );

View File

@ -21,6 +21,7 @@ import org.hibernate.service.ServiceRegistry;
* Automatically adds entity names, that have been changed during current revision, to revision entity.
*
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
* @author Chris Cranford
*
* @see org.hibernate.envers.ModifiedEntityNames
* @see org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity
@ -33,11 +34,10 @@ public class DefaultTrackingModifiedEntitiesRevisionInfoGenerator extends Defaul
String revisionInfoEntityName,
Class<?> revisionInfoClass,
Class<? extends RevisionListener> listenerClass,
PropertyData revisionInfoTimestampData,
boolean timestampAsDate,
RevisionTimestampValueResolver timestampValueResolver,
PropertyData modifiedEntityNamesData,
ServiceRegistry serviceRegistry) {
super( revisionInfoEntityName, revisionInfoClass, listenerClass, revisionInfoTimestampData, timestampAsDate, serviceRegistry );
super( revisionInfoEntityName, revisionInfoClass, listenerClass, timestampValueResolver, serviceRegistry );
modifiedEntityNamesSetter = ReflectionTools.getSetter( revisionInfoClass, modifiedEntityNamesData, serviceRegistry );
modifiedEntityNamesGetter = ReflectionTools.getGetter( revisionInfoClass, modifiedEntityNamesData, serviceRegistry );
}

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.envers.internal.revisioninfo;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Locale;
import java.util.Set;
@ -28,16 +29,15 @@ public class RevisionInfoQueryCreator {
private final String revisionInfoEntityName;
private final String revisionInfoIdName;
private final String revisionInfoTimestampName;
private final boolean timestampAsDate;
private final RevisionTimestampValueResolver timestampValueResolver;
public RevisionInfoQueryCreator(
String revisionInfoEntityName, String revisionInfoIdName,
String revisionInfoTimestampName, boolean timestampAsDate) {
String revisionInfoEntityName,
String revisionInfoIdName,
RevisionTimestampValueResolver timestampValueResolver) {
this.revisionInfoEntityName = revisionInfoEntityName;
this.revisionInfoIdName = revisionInfoIdName;
this.revisionInfoTimestampName = revisionInfoTimestampName;
this.timestampAsDate = timestampAsDate;
this.timestampValueResolver = timestampValueResolver;
}
public Query<?> getRevisionDateQuery(Session session, Number revision) {
@ -45,7 +45,7 @@ public class RevisionInfoQueryCreator {
String.format(
Locale.ENGLISH,
REVISION_DATE_QUERY,
revisionInfoTimestampName,
timestampValueResolver.getName(),
revisionInfoEntityName,
revisionInfoIdName
)
@ -59,9 +59,21 @@ public class RevisionInfoQueryCreator {
REVISION_NUMBER_FOR_DATE_QUERY,
revisionInfoIdName,
revisionInfoEntityName,
revisionInfoTimestampName
timestampValueResolver.getName()
)
).setParameter( REVISION_NUMBER_FOR_DATE_QUERY_PARAMETER, timestampAsDate ? date : date.getTime() );
).setParameter( REVISION_NUMBER_FOR_DATE_QUERY_PARAMETER, timestampValueResolver.resolveByValue( date ) );
}
public Query<?> getRevisionNumberForDateQuery(Session session, LocalDateTime localDateTime) {
return session.createQuery(
String.format(
Locale.ENGLISH,
REVISION_NUMBER_FOR_DATE_QUERY,
revisionInfoIdName,
revisionInfoEntityName,
timestampValueResolver.getName()
)
).setParameter( REVISION_NUMBER_FOR_DATE_QUERY_PARAMETER, timestampValueResolver.resolveByValue( localDateTime ) );
}
public Query<?> getRevisionsQuery(Session session, Set<Number> revisions) {

View File

@ -0,0 +1,77 @@
/*
* 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.envers.internal.revisioninfo;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import org.hibernate.envers.internal.entities.RevisionTimestampData;
import org.hibernate.envers.internal.tools.ReflectionTools;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.service.ServiceRegistry;
/**
* @author Chris Cranford
* @since 6.0
*/
public class RevisionTimestampValueResolver {
private final RevisionTimestampData timestampData;
private final Setter revisionTimestampSetter;
public RevisionTimestampValueResolver(Class<?> revisionInfoClass, RevisionTimestampData timestampData, ServiceRegistry serviceRegistry) {
this.timestampData = timestampData;
this.revisionTimestampSetter = ReflectionTools.getSetter( revisionInfoClass, timestampData, serviceRegistry );
}
public String getName() {
return timestampData.getName();
}
public void resolveNow(Object object) {
if ( timestampData.isTimestampDate() ) {
revisionTimestampSetter.set( object, new Date(), null );
}
else if ( timestampData.isTimestampLocalDateTime() ) {
revisionTimestampSetter.set(object, LocalDateTime.now(), null );
}
else {
revisionTimestampSetter.set( object, System.currentTimeMillis(), null );
}
}
public Object resolveByValue(Date date) {
if ( date != null ) {
if ( timestampData.isTimestampDate() ) {
return date;
}
else if ( timestampData.isTimestampLocalDateTime() ) {
return LocalDateTime.ofInstant( date.toInstant(), ZoneId.systemDefault() );
}
else {
return date.getTime();
}
}
return null;
}
public Object resolveByValue(LocalDateTime localDateTime) {
if ( localDateTime != null ) {
if ( timestampData.isTimestampDate() ) {
return Date.from( localDateTime.atZone( ZoneId.systemDefault() ).toInstant() );
}
else if ( timestampData.isTimestampLocalDateTime() ) {
return localDateTime;
}
else {
return localDateTime.atZone( ZoneId.systemDefault() ).toInstant().getEpochSecond();
}
}
return null;
}
}

View File

@ -0,0 +1,80 @@
/*
* 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.envers.test.entities.reventity;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Objects;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
import org.hibernate.envers.RevisionEntity;
import org.hibernate.envers.RevisionNumber;
import org.hibernate.envers.RevisionTimestamp;
/**
* @author Chris Cranford
*/
@Entity
@GenericGenerator(name = "EnversTestingRevisionGenerator",
strategy = "org.hibernate.id.enhanced.TableGenerator",
parameters = {
@Parameter(name = "table_name", value = "REVISION_GENERATOR"),
@Parameter(name = "initial_value", value = "1"),
@Parameter(name = "increment_size", value = "1"),
@Parameter(name = "prefer_entity_table_as_segment_value", value = "true")
}
)
@RevisionEntity
public class CustomLocalDateTimeRevEntity {
@Id
@GeneratedValue(generator = "EnversTestingRevisionGenerator")
@RevisionNumber
private int id;
@RevisionTimestamp
private LocalDateTime localDateTimestamp;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public LocalDateTime getLocalDateTimestamp() {
return localDateTimestamp;
}
public void setLocalDateTimestamp(LocalDateTime localDateTimestamp) {
this.localDateTimestamp = localDateTimestamp;
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + ( localDateTimestamp != null ? localDateTimestamp.hashCode() : 0 );
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CustomLocalDateTimeRevEntity that = (CustomLocalDateTimeRevEntity) o;
return id == that.id && Objects.equals(localDateTimestamp, that.localDateTimestamp);
}
}

View File

@ -0,0 +1,92 @@
/*
* 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.envers.integration.reventity;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import jakarta.persistence.EntityManager;
import org.hibernate.envers.test.entities.reventity.CustomLocalDateTimeRevEntity;
import org.hibernate.testing.TestForIssue;
import org.hibernate.orm.test.envers.BaseEnversJPAFunctionalTestCase;
import org.hibernate.orm.test.envers.Priority;
import org.hibernate.orm.test.envers.entities.StrTestEntity;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Chris Cranford
*/
@TestForIssue( jiraKey = "HHH-10496" )
public class LocalDateTimeTest extends BaseEnversJPAFunctionalTestCase {
private Instant timestampStart;
private Instant timestampEnd;
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
StrTestEntity.class,
CustomLocalDateTimeRevEntity.class
};
}
@Test
@Priority(10)
public void initData() {
EntityManager em = getEntityManager();
try {
timestampStart = Instant.now();
// some DBMs truncate time to seconds.
Thread.sleep( 1100 );
StrTestEntity entity = new StrTestEntity( "x" );
// Revision 1
em.getTransaction().begin();
em.persist( entity );
em.getTransaction().commit();
timestampEnd = Instant.now();
}
catch( InterruptedException x ) {
fail( "Unexpected interrupted exception" );
}
finally {
em.close();
}
}
@Test
public void testTimestampsUsingDate() {
// expect just one revision prior to this timestamp.
assertEquals( 1, getAuditReader().getRevisionNumberForDate( Date.from( timestampEnd ) ) );
}
@Test
public void testRevisionEntityLocalDateTime() {
// get revision
CustomLocalDateTimeRevEntity revInfo = getAuditReader().findRevision( CustomLocalDateTimeRevEntity.class, 1 );
assertNotNull( revInfo );
// verify started before revision timestamp
final LocalDateTime started = LocalDateTime.ofInstant( timestampStart, ZoneId.systemDefault() );
assertTrue( started.isBefore( revInfo.getLocalDateTimestamp() ) );
// verify ended after revision timestamp
final LocalDateTime ended = LocalDateTime.ofInstant( timestampEnd, ZoneId.systemDefault() );
assertTrue( ended.isAfter( revInfo.getLocalDateTimestamp() ) );
}
}