diff --git a/documentation/src/main/asciidoc/userguide/chapters/envers/Envers.adoc b/documentation/src/main/asciidoc/userguide/chapters/envers/Envers.adoc index 66a605fc35..841e0d55f8 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/envers/Envers.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/envers/Envers.adoc @@ -257,6 +257,10 @@ This property is only evaluated if the `ValidityAuditStrategy` is used. Column name of the timestamp of the end revision until which the data was valid. Only used if the `ValidityAuditStrategy` is used, and `org.hibernate.envers.audit_strategy_validity_store_revend_timestamp` evaluates to true. +`*org.hibernate.envers.audit_strategy_validity_revend_timestamp_numeric*`(default: `false` ):: +Boolean flag that controls whether the revision end timestamp field is treated as a `Long` data type. +Only used if the `ValidityAuditStrategy` is used, and `org.hibernate.envers.audit_strategy_validity_store_revend_timestamp` evaluates to true. + `*org.hibernate.envers.use_revision_entity_with_native_id*` (default: `true` ):: Boolean flag that determines the strategy of revision number generation. Default implementation of revision entity uses native identifier generator. @@ -320,6 +324,7 @@ The following configuration options have been added recently and should be regar . `org.hibernate.envers.modified_column_naming_strategy` . `org.hibernate.envers.original_id_prop_name` . `org.hibernate.envers.find_by_revision_exact_match` +. `org.hibernate.envers.audit_strategy_validity_revend_timestamp_numeric` ==== [[envers-additional-mappings]] @@ -1489,6 +1494,8 @@ Optionally, you can also override the default values using following properties: `org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name` +`org.hibernate.envers.audit_strategy_validity_revend_timestamp_numeric` + For more information, see <>. ==== diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/Configuration.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/Configuration.java index 404b2ad0d5..118cdad266 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/Configuration.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/Configuration.java @@ -90,6 +90,7 @@ public class Configuration { private final String revisionEndTimestampFieldName; private final String embeddableSetOrdinalPropertyName; private final boolean revisionEndTimestampEnabled; + private final boolean revisionEndTimestampNumeric; private final Map customAuditTableNames = new HashMap<>(); @@ -152,9 +153,14 @@ public class Configuration { EnversSettings.AUDIT_STRATEGY_VALIDITY_REVEND_TIMESTAMP_FIELD_NAME, DEFAULT_REV_TSTMP_FIELD ); + revisionEndTimestampNumeric = configProps.getBoolean( + EnversSettings.AUDIT_STRATEGY_VALIDITY_REVEND_TIMESTAMP_NUMERIC, + false + ); } else { revisionEndTimestampFieldName = null; + revisionEndTimestampNumeric = false; } embeddableSetOrdinalPropertyName = configProps.getString( @@ -219,6 +225,10 @@ public class Configuration { return revisionEndTimestampEnabled; } + public boolean isRevisionEndTimestampNumeric() { + return revisionEndTimestampNumeric; + } + public String getDefaultCatalogName() { return defaultCatalogName; } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/EnversSettings.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/EnversSettings.java index 57da14104a..f65d58884b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/EnversSettings.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/EnversSettings.java @@ -110,6 +110,14 @@ public interface EnversSettings { */ String AUDIT_STRATEGY_VALIDITY_REVEND_TIMESTAMP_FIELD_NAME = "org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name"; + /** + * Determines whether the timestamp of the end revision is stored as a numeric data type. + * Defaults to {@literal false}. + * + * @since 6.0 + */ + String AUDIT_STRATEGY_VALIDITY_REVEND_TIMESTAMP_NUMERIC = "org.hibernate.envers.audit_strategy_validity_revend_timestamp_numeric"; + /** * Name of column used for storing ordinal of the change in sets of embeddable elements. Defaults to {@literal SETORDINAL}. */ diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/ValidityAuditStrategy.java b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/ValidityAuditStrategy.java index edcf368c37..c00991575e 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/ValidityAuditStrategy.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/ValidityAuditStrategy.java @@ -109,7 +109,13 @@ public class ValidityAuditStrategy implements AuditStrategy { if ( mappingContext.getConfiguration().isRevisionEndTimestampEnabled() ) { // add a column for the timestamp of the end revision - final String revisionInfoTimestampTypeName = StandardBasicTypes.TIMESTAMP.getName(); + final String revisionInfoTimestampTypeName; + if ( mappingContext.getConfiguration().isRevisionEndTimestampNumeric() ) { + revisionInfoTimestampTypeName = StandardBasicTypes.LONG.getName(); + } + else { + revisionInfoTimestampTypeName = StandardBasicTypes.TIMESTAMP.getName(); + } final BasicAttribute revEndTimestampMapping = new BasicAttribute( mappingContext.getConfiguration().getRevisionEndTimestampFieldName(), revisionInfoTimestampTypeName, @@ -225,13 +231,11 @@ public class ValidityAuditStrategy implements AuditStrategy { // set [, REVEND_TSTMP = ?] if ( isRevisionEndTimestampEnabled ) { final Object revEndTimestampObj = revisionTimestampGetter.get( revision ); - final Date revisionEndTimestamp = convertRevEndTimestampToDate( revEndTimestampObj ); + final Object revEndValue = getRevEndTimestampValue( configuration, revEndTimestampObj ); final Type revEndTsType = rootAuditedEntityQueryable.getPropertyType( configuration.getRevisionEndTimestampFieldName() ); - revEndTsType.nullSafeSet( - preparedStatement, revisionEndTimestamp, index, sessionImplementor - ); + revEndTsType.nullSafeSet( preparedStatement, revEndValue, index, sessionImplementor ); index += revEndTsType.getColumnSpan( sessionImplementor.getFactory() ); } @@ -445,6 +449,20 @@ public class ValidityAuditStrategy implements AuditStrategy { return new Date( (Long) revEndTimestampObj ); } + private Long convertRevEndTimestampToLong(Object revEndTimstampObj) { + if ( revEndTimstampObj instanceof Date ) { + return ( (Date) revEndTimstampObj ).getTime(); + } + return (Long) revEndTimstampObj; + } + + private Object getRevEndTimestampValue(Configuration configuration, Object value) { + if ( configuration.isRevisionEndTimestampNumeric() ) { + return convertRevEndTimestampToLong( value ); + } + return convertRevEndTimestampToDate( value ); + } + private Queryable getQueryable(String entityName, SessionImplementor sessionImplementor) { return (Queryable) sessionImplementor.getFactory().getMetamodel().entityPersister( entityName ); } diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/strategy/RevisionEndNumericTypeTest.java b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/strategy/RevisionEndNumericTypeTest.java new file mode 100644 index 0000000000..6c9dd40afc --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/strategy/RevisionEndNumericTypeTest.java @@ -0,0 +1,61 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.envers.integration.strategy; + +import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Map; + +import org.hibernate.envers.configuration.EnversSettings; + +import org.hibernate.envers.strategy.internal.ValidityAuditStrategy; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.orm.test.envers.BaseEnversJPAFunctionalTestCase; +import org.hibernate.orm.test.envers.entities.StrTestEntity; +import org.hibernate.type.BasicType; +import org.junit.Test; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.envers.RequiresAuditStrategy; + +/** + * Tests the {@code REVEND} functionality using a {@code LONG} data type. + * This is only applicable with the ValidityAuditStrategy. + * + * @author Chris Cranford + */ +@TestForIssue( jiraKey = "HHH-6210" ) +@RequiresAuditStrategy( value = ValidityAuditStrategy.class, jiraKey = "HHH-6210" ) +public class RevisionEndNumericTypeTest extends BaseEnversJPAFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { StrTestEntity.class }; + } + + @Override + protected void addConfigOptions(Map options) { + super.addConfigOptions( options ); + options.put( EnversSettings.AUDIT_STRATEGY_VALIDITY_STORE_REVEND_TIMESTAMP, "true" ); + options.put( EnversSettings.AUDIT_STRATEGY_VALIDITY_REVEND_TIMESTAMP_NUMERIC, "true" ); + } + + + @Test + public void testRevisionEndTimestampIsTimestampType() { + // get the entity and verify the revision end timestamp property exists + final PersistentClass clazz = metadata().getEntityBinding( StrTestEntity.class.getName() + "_AUD" ); + assertTrue( clazz.hasProperty( "REVEND_TSTMP" ) ); + + final Property property = clazz.getProperty( "REVEND_TSTMP" ); + assertTyping( BasicType.class, clazz.getProperty( "REVEND_TSTMP" ).getType() ); + assertEquals( Long.class, ( (BasicType) property.getType() ).getJavaType() ); + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/strategy/RevisionEndTimestampTypeTest.java b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/strategy/RevisionEndTimestampTypeTest.java new file mode 100644 index 0000000000..a3ce1a94cc --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/strategy/RevisionEndTimestampTypeTest.java @@ -0,0 +1,57 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.envers.integration.strategy; + +import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; +import static org.junit.Assert.assertEquals; + +import java.sql.Timestamp; +import java.util.Map; + +import org.hibernate.envers.configuration.EnversSettings; + +import org.hibernate.envers.strategy.internal.ValidityAuditStrategy; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.orm.test.envers.BaseEnversJPAFunctionalTestCase; +import org.hibernate.orm.test.envers.entities.StrTestEntity; +import org.hibernate.type.BasicType; +import org.junit.Test; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.envers.RequiresAuditStrategy; + +/** + * Tests the {@code REVEND} functionality using a {@code LONG} data type. + * This is only applicable with the ValidityAuditStrategy. + * + * @author Chris Cranford + */ +@TestForIssue( jiraKey = "HHH-6210" ) +@RequiresAuditStrategy( value = ValidityAuditStrategy.class, jiraKey = "HHH-6210" ) +public class RevisionEndTimestampTypeTest extends BaseEnversJPAFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { StrTestEntity.class }; + } + + @Override + protected void addConfigOptions(Map options) { + options.put( EnversSettings.AUDIT_STRATEGY_VALIDITY_STORE_REVEND_TIMESTAMP, "true" ); + } + + @Test + public void testRevisionEndTimestampIsLongType() { + // get the entity and verify the revision end timestamp property exists + final PersistentClass clazz = metadata().getEntityBinding( StrTestEntity.class.getName() + "_AUD" ); + + final Property property = clazz.getProperty( "REVEND_TSTMP" ); + assertTyping( BasicType.class, property.getType() ); + assertEquals( Timestamp.class, ( (BasicType) property.getType() ).getJavaType() ); + } +}