HHH-6210 Added config option to allow revend timestamp fields to be long data types

This commit is contained in:
Chris Cranford 2021-11-26 00:49:33 -05:00
parent 1abf044f2e
commit 205f0ce9bf
6 changed files with 166 additions and 5 deletions

View File

@ -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. 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. 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` ):: `*org.hibernate.envers.use_revision_entity_with_native_id*` (default: `true` )::
Boolean flag that determines the strategy of revision number generation. Boolean flag that determines the strategy of revision number generation.
Default implementation of revision entity uses native identifier generator. 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.modified_column_naming_strategy`
. `org.hibernate.envers.original_id_prop_name` . `org.hibernate.envers.original_id_prop_name`
. `org.hibernate.envers.find_by_revision_exact_match` . `org.hibernate.envers.find_by_revision_exact_match`
. `org.hibernate.envers.audit_strategy_validity_revend_timestamp_numeric`
==== ====
[[envers-additional-mappings]] [[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_field_name`
`org.hibernate.envers.audit_strategy_validity_revend_timestamp_numeric`
For more information, see <<envers-configuration>>. For more information, see <<envers-configuration>>.
==== ====

View File

@ -90,6 +90,7 @@ public class Configuration {
private final String revisionEndTimestampFieldName; private final String revisionEndTimestampFieldName;
private final String embeddableSetOrdinalPropertyName; private final String embeddableSetOrdinalPropertyName;
private final boolean revisionEndTimestampEnabled; private final boolean revisionEndTimestampEnabled;
private final boolean revisionEndTimestampNumeric;
private final Map<String, String> customAuditTableNames = new HashMap<>(); private final Map<String, String> customAuditTableNames = new HashMap<>();
@ -152,9 +153,14 @@ public Configuration(Properties properties, EnversService enversService, Metadat
EnversSettings.AUDIT_STRATEGY_VALIDITY_REVEND_TIMESTAMP_FIELD_NAME, EnversSettings.AUDIT_STRATEGY_VALIDITY_REVEND_TIMESTAMP_FIELD_NAME,
DEFAULT_REV_TSTMP_FIELD DEFAULT_REV_TSTMP_FIELD
); );
revisionEndTimestampNumeric = configProps.getBoolean(
EnversSettings.AUDIT_STRATEGY_VALIDITY_REVEND_TIMESTAMP_NUMERIC,
false
);
} }
else { else {
revisionEndTimestampFieldName = null; revisionEndTimestampFieldName = null;
revisionEndTimestampNumeric = false;
} }
embeddableSetOrdinalPropertyName = configProps.getString( embeddableSetOrdinalPropertyName = configProps.getString(
@ -219,6 +225,10 @@ public boolean isRevisionEndTimestampEnabled() {
return revisionEndTimestampEnabled; return revisionEndTimestampEnabled;
} }
public boolean isRevisionEndTimestampNumeric() {
return revisionEndTimestampNumeric;
}
public String getDefaultCatalogName() { public String getDefaultCatalogName() {
return defaultCatalogName; return defaultCatalogName;
} }

View File

@ -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"; 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}. * Name of column used for storing ordinal of the change in sets of embeddable elements. Defaults to {@literal SETORDINAL}.
*/ */

View File

@ -109,7 +109,13 @@ public void addAdditionalColumns(MappingContext mappingContext) {
if ( mappingContext.getConfiguration().isRevisionEndTimestampEnabled() ) { if ( mappingContext.getConfiguration().isRevisionEndTimestampEnabled() ) {
// add a column for the timestamp of the end revision // 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( final BasicAttribute revEndTimestampMapping = new BasicAttribute(
mappingContext.getConfiguration().getRevisionEndTimestampFieldName(), mappingContext.getConfiguration().getRevisionEndTimestampFieldName(),
revisionInfoTimestampTypeName, revisionInfoTimestampTypeName,
@ -225,13 +231,11 @@ public Integer execute(Connection connection) throws SQLException {
// set [, REVEND_TSTMP = ?] // set [, REVEND_TSTMP = ?]
if ( isRevisionEndTimestampEnabled ) { if ( isRevisionEndTimestampEnabled ) {
final Object revEndTimestampObj = revisionTimestampGetter.get( revision ); final Object revEndTimestampObj = revisionTimestampGetter.get( revision );
final Date revisionEndTimestamp = convertRevEndTimestampToDate( revEndTimestampObj ); final Object revEndValue = getRevEndTimestampValue( configuration, revEndTimestampObj );
final Type revEndTsType = rootAuditedEntityQueryable.getPropertyType( final Type revEndTsType = rootAuditedEntityQueryable.getPropertyType(
configuration.getRevisionEndTimestampFieldName() configuration.getRevisionEndTimestampFieldName()
); );
revEndTsType.nullSafeSet( revEndTsType.nullSafeSet( preparedStatement, revEndValue, index, sessionImplementor );
preparedStatement, revisionEndTimestamp, index, sessionImplementor
);
index += revEndTsType.getColumnSpan( sessionImplementor.getFactory() ); index += revEndTsType.getColumnSpan( sessionImplementor.getFactory() );
} }
@ -445,6 +449,20 @@ private Date convertRevEndTimestampToDate(Object revEndTimestampObj) {
return new Date( (Long) revEndTimestampObj ); 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) { private Queryable getQueryable(String entityName, SessionImplementor sessionImplementor) {
return (Queryable) sessionImplementor.getFactory().getMetamodel().entityPersister( entityName ); return (Queryable) sessionImplementor.getFactory().getMetamodel().entityPersister( entityName );
} }

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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() );
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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() );
}
}