[hibernate-commits] Hibernate SVN: r20743 - in core/trunk/envers/src: main/java/org/hibernate/envers/configuration/metadata and 4 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Tue Sep 28 18:23:04 EDT 2010


Author: fbascheper
Date: 2010-09-28 18:23:04 -0400 (Tue, 28 Sep 2010)
New Revision: 20743

Added:
   core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/strategy/
   core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTestCustomRevEnt.java
   core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTsTest.java
Modified:
   core/trunk/envers/src/main/java/org/hibernate/envers/configuration/AuditConfiguration.java
   core/trunk/envers/src/main/java/org/hibernate/envers/configuration/AuditEntitiesConfiguration.java
   core/trunk/envers/src/main/java/org/hibernate/envers/configuration/RevisionInfoConfiguration.java
   core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java
   core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/MetadataTools.java
   core/trunk/envers/src/main/java/org/hibernate/envers/strategy/ValidityAuditStrategy.java
   core/trunk/envers/src/test/resources/testng.xml
Log:
HHH-5371 - Add support for REVEND_TSTMP which will enable audit table partitioning

Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/AuditConfiguration.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/AuditConfiguration.java	2010-09-28 16:39:06 UTC (rev 20742)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/AuditConfiguration.java	2010-09-28 22:23:04 UTC (rev 20743)
@@ -28,16 +28,18 @@
 import java.util.WeakHashMap;
 
 import org.hibernate.MappingException;
+import org.hibernate.annotations.common.reflection.ReflectionManager;
+import org.hibernate.cfg.Configuration;
 import org.hibernate.envers.entities.EntitiesConfigurations;
+import org.hibernate.envers.entities.PropertyData;
 import org.hibernate.envers.revisioninfo.RevisionInfoNumberReader;
 import org.hibernate.envers.revisioninfo.RevisionInfoQueryCreator;
+import org.hibernate.envers.strategy.AuditStrategy;
+import org.hibernate.envers.strategy.ValidityAuditStrategy;
 import org.hibernate.envers.synchronization.AuditProcessManager;
-import org.hibernate.envers.strategy.AuditStrategy;
+import org.hibernate.envers.tools.reflection.ReflectionTools;
+import org.hibernate.property.Getter;
 
-import org.hibernate.cfg.Configuration;
-import org.hibernate.cfg.AnnotationConfiguration;
-import org.hibernate.annotations.common.reflection.ReflectionManager;
-
 /**
  * @author Adam Warski (adam at warski dot org)
  * @author Stephanie Pau at Markit Group Plc
@@ -79,7 +81,6 @@
         return auditStrategy;
     }
 
-    @SuppressWarnings({ "unchecked" })
     public AuditConfiguration(Configuration cfg) {
         Properties properties = cfg.getProperties();
 
@@ -89,20 +90,33 @@
         auditEntCfg = new AuditEntitiesConfiguration(properties, revInfoCfgResult.getRevisionInfoEntityName());
         globalCfg = new GlobalConfiguration(properties);
         auditProcessManager = new AuditProcessManager(revInfoCfgResult.getRevisionInfoGenerator());
-
-        try {
-            Class auditStrategyClass = Thread.currentThread().getContextClassLoader().loadClass(auditEntCfg.getAuditStrategyName());
-            auditStrategy = (AuditStrategy) auditStrategyClass.newInstance();
-        } catch (Exception e) {
-           throw new MappingException(String.format("Unable to create AuditStrategy[%s] instance." , auditEntCfg.getAuditStrategyName()));
-        }
-        
         revisionInfoQueryCreator = revInfoCfgResult.getRevisionInfoQueryCreator();
         revisionInfoNumberReader = revInfoCfgResult.getRevisionInfoNumberReader();
+        auditStrategy = initializeAuditStrategy(revInfoCfgResult.getRevisionInfoClass(), 
+        		revInfoCfgResult.getRevisionInfoTimestampData());
         entCfg = new EntitiesConfigurator().configure(cfg, reflectionManager, globalCfg, auditEntCfg, auditStrategy,
                 revInfoCfgResult.getRevisionInfoXmlMapping(), revInfoCfgResult.getRevisionInfoRelationMapping());
     }
 
+	private AuditStrategy initializeAuditStrategy(Class<?> revisionInfoClass, PropertyData revisionInfoTimestampData) {
+		AuditStrategy strategy;
+		
+		try {
+            Class<?> auditStrategyClass = Thread.currentThread().getContextClassLoader().loadClass(auditEntCfg.getAuditStrategyName());
+            strategy = (AuditStrategy) auditStrategyClass.newInstance();
+        } catch (Exception e) {
+           throw new MappingException(String.format("Unable to create AuditStrategy[%s] instance." , auditEntCfg.getAuditStrategyName()));
+        }
+        
+        if (strategy instanceof ValidityAuditStrategy) {
+        	// further initialization required
+        	Getter revisionTimestampGetter = ReflectionTools.getGetter(revisionInfoClass, revisionInfoTimestampData);
+        	((ValidityAuditStrategy) strategy).setRevisionTimestampGetter(revisionTimestampGetter);
+        }
+
+        return strategy;
+	}
+
     //
 
     private static Map<Configuration, AuditConfiguration> cfgs

Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/AuditEntitiesConfiguration.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/AuditEntitiesConfiguration.java	2010-09-28 16:39:06 UTC (rev 20742)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/AuditEntitiesConfiguration.java	2010-09-28 22:23:04 UTC (rev 20743)
@@ -25,17 +25,11 @@
 
 import static org.hibernate.envers.tools.Tools.getProperty;
 
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 
-import org.hibernate.MappingException;
 import org.hibernate.envers.strategy.DefaultAuditStrategy;
-import org.hibernate.envers.strategy.ValidityAuditStrategy;
 
 /**
  * Configuration of versions entities - names of fields, entities and tables created to store versioning information.
@@ -61,6 +55,9 @@
     private final Map<String, String> customAuditTablesNames;
 
     private final String revisionEndFieldName;
+    
+    private final boolean revisionEndTimestampEnabled;
+    private final String revisionEndTimestampFieldName;
 
     public AuditEntitiesConfiguration(Properties properties, String revisionInfoEntityName) {
         this.revisionInfoEntityName = revisionInfoEntityName;
@@ -97,6 +94,21 @@
                 "org.hibernate.envers.audit_strategy_valid_time_end_name",
                 "REVEND");
 
+        String revisionEndTimestampEnabledStr = getProperty(properties,
+        		"org.hibernate.envers.audit_strategy_validity_store_revend_timestamp",
+        		"org.hibernate.envers.audit_strategy_validity_store_revend_timestamp",
+        		"false");
+        revisionEndTimestampEnabled = Boolean.parseBoolean(revisionEndTimestampEnabledStr);
+                
+        if (revisionEndTimestampEnabled) {
+            revisionEndTimestampFieldName = getProperty(properties,
+            		"org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name",
+            		"org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name",
+            		"REVEND_TSTMP");
+        } else {
+            revisionEndTimestampFieldName = null;
+        }
+        
         customAuditTablesNames = new HashMap<String, String>();
 
         revisionNumberPath = originalIdPropName + "." + revisionFieldName + ".id";
@@ -111,6 +123,14 @@
         return revisionFieldName;
     }
 
+	public boolean isRevisionEndTimestampEnabled() {
+		return revisionEndTimestampEnabled;
+	}
+
+	public String getRevisionEndTimestampFieldName() {
+		return revisionEndTimestampFieldName;
+	}
+    
     public String getRevisionNumberPath() {
         return revisionNumberPath;
     }

Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/RevisionInfoConfiguration.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/RevisionInfoConfiguration.java	2010-09-28 16:39:06 UTC (rev 20742)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/RevisionInfoConfiguration.java	2010-09-28 22:23:04 UTC (rev 20743)
@@ -29,7 +29,6 @@
 import org.dom4j.Document;
 import org.dom4j.DocumentHelper;
 import org.dom4j.Element;
-import org.hibernate.envers.Audited;
 import org.hibernate.envers.*;
 import org.hibernate.envers.entities.PropertyData;
 import org.hibernate.envers.configuration.metadata.MetadataTools;
@@ -176,7 +175,6 @@
                 "property");
     }
 
-    @SuppressWarnings({"unchecked"})
     public RevisionInfoConfigurationResult configure(Configuration cfg, ReflectionManager reflectionManager) {
         Iterator<PersistentClass> classes = (Iterator<PersistentClass>) cfg.getClassMappings();
         boolean revisionEntityFound = false;
@@ -245,7 +243,8 @@
                 new RevisionInfoQueryCreator(revisionInfoEntityName, revisionInfoIdData.getName(),
                         revisionInfoTimestampData.getName(), isTimestampAsDate()),
                 generateRevisionInfoRelationMapping(),
-                new RevisionInfoNumberReader(revisionInfoClass, revisionInfoIdData), revisionInfoEntityName);
+                new RevisionInfoNumberReader(revisionInfoClass, revisionInfoIdData), revisionInfoEntityName, 
+                revisionInfoClass, revisionInfoTimestampData);
     }
     
     private boolean isTimestampAsDate() {
@@ -261,17 +260,22 @@
     private final Element revisionInfoRelationMapping;
     private final RevisionInfoNumberReader revisionInfoNumberReader;
     private final String revisionInfoEntityName;
+    private final Class<?> revisionInfoClass;
+    private final PropertyData revisionInfoTimestampData;
 
     RevisionInfoConfigurationResult(RevisionInfoGenerator revisionInfoGenerator,
                                     Document revisionInfoXmlMapping, RevisionInfoQueryCreator revisionInfoQueryCreator,
                                     Element revisionInfoRelationMapping,
-                                    RevisionInfoNumberReader revisionInfoNumberReader, String revisionInfoEntityName) {
+                                    RevisionInfoNumberReader revisionInfoNumberReader, String revisionInfoEntityName,  Class<?> revisionInfoClass,
+                                    PropertyData revisionInfoTimestampData) {
         this.revisionInfoGenerator = revisionInfoGenerator;
         this.revisionInfoXmlMapping = revisionInfoXmlMapping;
         this.revisionInfoQueryCreator = revisionInfoQueryCreator;
         this.revisionInfoRelationMapping = revisionInfoRelationMapping;
         this.revisionInfoNumberReader = revisionInfoNumberReader;
         this.revisionInfoEntityName = revisionInfoEntityName;
+        this.revisionInfoClass = revisionInfoClass;
+        this.revisionInfoTimestampData = revisionInfoTimestampData;
     }
 
     public RevisionInfoGenerator getRevisionInfoGenerator() {
@@ -297,4 +301,13 @@
     public String getRevisionInfoEntityName() {
         return revisionInfoEntityName;
     }
+
+	public Class<?> getRevisionInfoClass() {
+		return revisionInfoClass;
+	}
+
+	public PropertyData getRevisionInfoTimestampData() {
+		return revisionInfoTimestampData;
+	}
+    
 }
\ No newline at end of file

Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java	2010-09-28 16:39:06 UTC (rev 20742)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java	2010-09-28 22:23:04 UTC (rev 20743)
@@ -55,6 +55,7 @@
 import org.hibernate.type.ComponentType;
 import org.hibernate.type.ManyToOneType;
 import org.hibernate.type.OneToOneType;
+import org.hibernate.type.TimestampType;
 import org.hibernate.type.Type;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -64,7 +65,7 @@
  * @author Sebastian Komander
  * @author Tomasz Bech
  * @author Stephanie Pau at Markit Group Plc
- * @author Hern�n Chanfreau
+ * @author Hern&aacute;n Chanfreau
  */
 public final class AuditMetadataGenerator {
     private static final Logger log = LoggerFactory.getLogger(AuditMetadataGenerator.class);
@@ -144,7 +145,7 @@
         addEndRevision(any_mapping);
     }
 
-    private void addEndRevision(Element any_mapping) {
+    private void addEndRevision(Element any_mapping ) {
         // Add the end-revision field, if the appropriate strategy is used.
         if (auditStrategy instanceof ValidityAuditStrategy) {
             Element end_rev_mapping = (Element) revisionInfoRelationMapping.clone();
@@ -153,10 +154,16 @@
             MetadataTools.addOrModifyColumn(end_rev_mapping, verEntCfg.getRevisionEndFieldName());
 
             any_mapping.add(end_rev_mapping);
+            
+            if (verEntCfg.isRevisionEndTimestampEnabled()) {
+            	// add a column for the timestamp of the end revision
+            	String revisionInfoTimestampSqlType = TimestampType.INSTANCE.getName();
+            	Element timestampProperty = MetadataTools.addProperty(any_mapping, verEntCfg.getRevisionEndTimestampFieldName(), revisionInfoTimestampSqlType, true, true, false);
+            	MetadataTools.addColumn(timestampProperty, verEntCfg.getRevisionEndTimestampFieldName(), 0, 0, 0, null);
+            }
         }
     }
 
-    @SuppressWarnings({"unchecked"})
     void addValue(Element parent, Value value, CompositeMapperBuilder currentMapper, String entityName,
                   EntityXmlMappingData xmlMappingData, PropertyAuditingData propertyAuditingData,
                   boolean insertable, boolean firstPass) {
@@ -204,7 +211,6 @@
         }
     }
 
-    @SuppressWarnings({"unchecked"})
     private void addProperties(Element parent, Iterator<Property> properties, CompositeMapperBuilder currentMapper,
                                ClassAuditingData auditingData, String entityName, EntityXmlMappingData xmlMappingData,
                                boolean firstPass) {

Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/MetadataTools.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/MetadataTools.java	2010-09-28 16:39:06 UTC (rev 20742)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/MetadataTools.java	2010-09-28 22:23:04 UTC (rev 20743)
@@ -49,7 +49,7 @@
         return id_mapping;
     }
 
-    public static Element addProperty(Element parent, String name, String type, boolean insertable, boolean key) {
+    public static Element addProperty(Element parent, String name, String type, boolean insertable, boolean updateable, boolean key) {
         Element prop_mapping;
         if (key) {
             prop_mapping = parent.addElement("key-property");
@@ -59,7 +59,7 @@
 
         prop_mapping.addAttribute("name", name);
         prop_mapping.addAttribute("insert", Boolean.toString(insertable));
-        prop_mapping.addAttribute("update", "false");
+        prop_mapping.addAttribute("update", Boolean.toString(updateable));
 
         if (type != null) {
             prop_mapping.addAttribute("type", type);
@@ -68,6 +68,10 @@
         return prop_mapping;
     }
 
+    public static Element addProperty(Element parent, String name, String type, boolean insertable, boolean key) {
+        return addProperty(parent, name, type, insertable, false, key);
+    }
+
     private static void addOrModifyAttribute(Element parent, String name, String value) {
         Attribute attribute = parent.attribute(name);
         if (attribute == null) {

Modified: core/trunk/envers/src/main/java/org/hibernate/envers/strategy/ValidityAuditStrategy.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/strategy/ValidityAuditStrategy.java	2010-09-28 16:39:06 UTC (rev 20742)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/strategy/ValidityAuditStrategy.java	2010-09-28 22:23:04 UTC (rev 20743)
@@ -1,6 +1,7 @@
 package org.hibernate.envers.strategy;
 
 import java.io.Serializable;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -15,6 +16,7 @@
 import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
 import org.hibernate.envers.tools.query.Parameters;
 import org.hibernate.envers.tools.query.QueryBuilder;
+import org.hibernate.property.Getter;
 
 /**
  *  Audit strategy which persists and retrieves audit information using a validity algorithm, based on the 
@@ -39,7 +41,11 @@
  * @author Adam Warski (adam at warski dot org)
  */
 public class ValidityAuditStrategy implements AuditStrategy {
-    public void perform(Session session, String entityName, AuditConfiguration auditCfg, Serializable id, Object data,
+	
+	/** getter for the revision entity field annotated with @RevisionTimestamp */ 
+	private Getter revisionTimestampGetter = null;
+	
+	public void perform(Session session, String entityName, AuditConfiguration auditCfg, Serializable id, Object data,
                         Object revision) {
         AuditEntitiesConfiguration audEntCfg = auditCfg.getAuditEntCfg();
         String auditedEntityName = audEntCfg.getAuditEntityName(entityName);
@@ -68,27 +74,22 @@
     public void performCollectionChange(Session session, AuditConfiguration auditCfg,
                                         PersistentCollectionChangeData persistentCollectionChangeData, Object revision) {
         // Update the end date of the previous row if this operation is expected to have a previous row
-        if (getRevisionType(auditCfg, persistentCollectionChangeData.getData()) != RevisionType.ADD) {
-            /*
-             Constructing a query (there are multiple id fields):
-             select e from audited_middle_ent e where e.end_rev is null and e.id1 = :id1 and e.id2 = :id2 ...
-             */
+        // Constructing a query (there are multiple id fields):
+        // select e from audited_middle_ent e where e.end_rev is null and e.id1 = :id1 and e.id2 = :id2 ...
 
-            QueryBuilder qb = new QueryBuilder(persistentCollectionChangeData.getEntityName(), "e");
+        QueryBuilder qb = new QueryBuilder(persistentCollectionChangeData.getEntityName(), "e");
 
-            // Adding a parameter for each id component, except the rev number
-            String originalIdPropName = auditCfg.getAuditEntCfg().getOriginalIdPropName();
-            Map<String, Object> originalId = (Map<String, Object>) persistentCollectionChangeData.getData().get(
+        // Adding a parameter for each id component, except the rev number
+        String originalIdPropName = auditCfg.getAuditEntCfg().getOriginalIdPropName();
+        Map<String, Object> originalId = (Map<String, Object>) persistentCollectionChangeData.getData().get(
                     originalIdPropName);
-            for (Map.Entry<String, Object> originalIdEntry : originalId.entrySet()) {
-                if (!auditCfg.getAuditEntCfg().getRevisionFieldName().equals(originalIdEntry.getKey())) {
-                    qb.getRootParameters().addWhereWithParam(originalIdPropName + "." + originalIdEntry.getKey(),
-                            true, "=", originalIdEntry.getValue());
-                }
-            }
-
-            updateLastRevision(session, auditCfg, qb, originalId, persistentCollectionChangeData.getEntityName(), revision);
+        for (Map.Entry<String, Object> originalIdEntry : originalId.entrySet()) {
+           if (!auditCfg.getAuditEntCfg().getRevisionFieldName().equals(originalIdEntry.getKey())) {
+              qb.getRootParameters().addWhereWithParam(originalIdPropName + "." + originalIdEntry.getKey(),
+              true, "=", originalIdEntry.getValue());
+           }
         }
+        updateLastRevision(session, auditCfg, qb, originalId, persistentCollectionChangeData.getEntityName(), revision);
 
         // Save the audit data
         session.save(persistentCollectionChangeData.getEntityName(), persistentCollectionChangeData.getData());
@@ -110,7 +111,11 @@
 		addRevisionRestriction(rootParameters, revisionProperty, revisionEndProperty, addAlias);
 	}
     
-    private void addRevisionRestriction(Parameters rootParameters,  
+	public void setRevisionTimestampGetter(Getter revisionTimestampGetter) {
+		this.revisionTimestampGetter = revisionTimestampGetter;
+	}
+
+	private void addRevisionRestriction(Parameters rootParameters,  
 			String revisionProperty, String revisionEndProperty, boolean addAlias) {
     	
 		// e.revision <= _revision and (e.endRevision > _revision or e.endRevision is null)
@@ -129,7 +134,7 @@
     private void updateLastRevision(Session session, AuditConfiguration auditCfg, QueryBuilder qb,
                                     Object id, String auditedEntityName, Object revision) {
         String revisionEndFieldName = auditCfg.getAuditEntCfg().getRevisionEndFieldName();
-
+        
         // e.end_rev is null
         qb.getRootParameters().addWhere(revisionEndFieldName, true, "is", "null", false);
 
@@ -141,10 +146,28 @@
             Object previousData = l.get(0);
             ((Map<String, Object>) previousData).put(revisionEndFieldName, revision);
 
+            if (auditCfg.getAuditEntCfg().isRevisionEndTimestampEnabled()) {
+                // Determine the value of the revision property annotated with @RevisionTimestamp
+            	Date revisionEndTimestamp;
+            	String revEndTimestampFieldName = auditCfg.getAuditEntCfg().getRevisionEndTimestampFieldName();
+            	Object revEndTimestampObj = this.revisionTimestampGetter.get(revision);
+
+            	// convert to a java.util.Date
+            	if (revEndTimestampObj instanceof Date) {
+            		revisionEndTimestamp = (Date) revEndTimestampObj;
+            	} else {
+            		revisionEndTimestamp = new Date((Long) revEndTimestampObj);
+            	}
+
+            	// Setting the end revision timestamp
+            	((Map<String, Object>) previousData).put(revEndTimestampFieldName, revisionEndTimestamp);
+            }
+            
             // Saving the previous version
             session.save(auditedEntityName, previousData);
-        } else {
-            throw new RuntimeException("Cannot find previous revision for entity " + auditedEntityName + " and id " + id);
+
+        } else if(l.size() > 1) {
+            throw new RuntimeException("Cannot find previous revision for entity " + auditedEntityName + " and id " + id + " received " + l.size() + " rows back");
         }
     }
 }

Added: core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTestCustomRevEnt.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTestCustomRevEnt.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTestCustomRevEnt.java	2010-09-28 22:23:04 UTC (rev 20743)
@@ -0,0 +1,433 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.envers.test.integration.strategy;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+
+import org.hibernate.Session;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.envers.strategy.ValidityAuditStrategy;
+import org.hibernate.envers.test.AbstractEntityTest;
+import org.hibernate.envers.test.entities.manytomany.sametable.Child1Entity;
+import org.hibernate.envers.test.entities.manytomany.sametable.Child2Entity;
+import org.hibernate.envers.test.entities.manytomany.sametable.ParentEntity;
+import org.hibernate.envers.test.entities.reventity.CustomDateRevEntity;
+import org.hibernate.envers.test.tools.TestTools;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ * Test which checks that the revision end timestamp is correctly set for
+ * {@link ValidityAuditStrategy}.
+ * 
+ * @author Erik-Berndt Scheper
+ */
+public class ValidityAuditStrategyRevEndTestCustomRevEnt extends AbstractEntityTest {
+	private final String revendTimestampColumName = "REVEND_TIMESTAMP";
+
+	private Integer p1_id;
+	private Integer p2_id;
+	private Integer c1_1_id;
+	private Integer c1_2_id;
+	private Integer c2_1_id;
+	private Integer c2_2_id;
+	private Map<Number, CustomDateRevEntity> revisions;
+
+	public void configure(Ejb3Configuration cfg) {
+		cfg.addAnnotatedClass(ParentEntity.class);
+		cfg.addAnnotatedClass(Child1Entity.class);
+		cfg.addAnnotatedClass(Child2Entity.class);
+        cfg.addAnnotatedClass(CustomDateRevEntity.class);
+
+		cfg.setProperty("org.hibernate.envers.audit_strategy",
+				"org.hibernate.envers.strategy.ValidityAuditStrategy");
+		cfg
+				.setProperty(
+						"org.hibernate.envers.audit_strategy_validity_store_revend_timestamp",
+						"true");
+		cfg
+				.setProperty(
+						"org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name",
+						revendTimestampColumName);
+	}
+
+	@BeforeClass(enabled = true, dependsOnMethods = "init")
+	public void initData() {
+		EntityManager em = getEntityManager();
+
+		// We need first to modify the columns in the middle (join table) to
+		// allow null values. Hbm2ddl doesn't seem
+		// to allow this.
+		em.getTransaction().begin();
+		Session session = (Session) em.getDelegate();
+		session.createSQLQuery("DROP TABLE children").executeUpdate();
+		session
+				.createSQLQuery(
+						"CREATE TABLE children(parent_id integer, child1_id integer NULL, child2_id integer NULL)")
+				.executeUpdate();
+		session.createSQLQuery("DROP TABLE children_AUD").executeUpdate();
+		session
+				.createSQLQuery(
+						"CREATE TABLE children_AUD(REV integer NOT NULL, REVEND integer, "
+								+ revendTimestampColumName
+								+ " timestamp, REVTYPE tinyint, "
+								+ "parent_id integer, child1_id integer NULL, child2_id integer NULL)")
+				.executeUpdate();
+		em.getTransaction().commit();
+		em.clear();
+
+		ParentEntity p1 = new ParentEntity("parent_1");
+		ParentEntity p2 = new ParentEntity("parent_2");
+
+		Child1Entity c1_1 = new Child1Entity("child1_1");
+		Child1Entity c1_2 = new Child1Entity("child1_2");
+
+		Child2Entity c2_1 = new Child2Entity("child2_1");
+		Child2Entity c2_2 = new Child2Entity("child2_2");
+
+		// Revision 1
+		em.getTransaction().begin();
+
+		em.persist(p1);
+		em.persist(p2);
+		em.persist(c1_1);
+		em.persist(c1_2);
+		em.persist(c2_1);
+		em.persist(c2_2);
+
+		em.getTransaction().commit();
+		em.clear();
+
+		// Revision 2 - (p1: c1_1, p2: c2_1)
+
+		em.getTransaction().begin();
+
+		p1 = em.find(ParentEntity.class, p1.getId());
+		p2 = em.find(ParentEntity.class, p2.getId());
+		c1_1 = em.find(Child1Entity.class, c1_1.getId());
+		c2_1 = em.find(Child2Entity.class, c2_1.getId());
+
+		p1.getChildren1().add(c1_1);
+		p2.getChildren2().add(c2_1);
+
+		em.getTransaction().commit();
+		em.clear();
+
+		// Revision 3 - (p1: c1_1, c1_2, c2_2, p2: c1_1, c2_1)
+		em.getTransaction().begin();
+
+		p1 = em.find(ParentEntity.class, p1.getId());
+		p2 = em.find(ParentEntity.class, p2.getId());
+		c1_1 = em.find(Child1Entity.class, c1_1.getId());
+		c1_2 = em.find(Child1Entity.class, c1_2.getId());
+		c2_2 = em.find(Child2Entity.class, c2_2.getId());
+
+		p1.getChildren1().add(c1_2);
+		p1.getChildren2().add(c2_2);
+
+		p2.getChildren1().add(c1_1);
+
+		em.getTransaction().commit();
+		em.clear();
+
+		// Revision 4 - (p1: c1_2, c2_2, p2: c1_1, c2_1, c2_2)
+		em.getTransaction().begin();
+
+		p1 = em.find(ParentEntity.class, p1.getId());
+		p2 = em.find(ParentEntity.class, p2.getId());
+		c1_1 = em.find(Child1Entity.class, c1_1.getId());
+		c2_2 = em.find(Child2Entity.class, c2_2.getId());
+
+		p1.getChildren1().remove(c1_1);
+		p2.getChildren2().add(c2_2);
+
+		em.getTransaction().commit();
+		em.clear();
+
+		// Revision 5 - (p1: c2_2, p2: c1_1, c2_1)
+		em.getTransaction().begin();
+
+		p1 = em.find(ParentEntity.class, p1.getId());
+		p2 = em.find(ParentEntity.class, p2.getId());
+		c1_2 = em.find(Child1Entity.class, c1_2.getId());
+		c2_2 = em.find(Child2Entity.class, c2_2.getId());
+
+		c2_2.getParents().remove(p2);
+		c1_2.getParents().remove(p1);
+
+		em.getTransaction().commit();
+		em.clear();
+
+		//
+
+		p1_id = p1.getId();
+		p2_id = p2.getId();
+		c1_1_id = c1_1.getId();
+		c1_2_id = c1_2.getId();
+		c2_1_id = c2_1.getId();
+		c2_2_id = c2_2.getId();
+
+		Set<Number> revisionNumbers = new HashSet<Number>();
+		revisionNumbers.addAll(Arrays.asList(1, 2, 3, 4, 5));
+		revisions = getAuditReader().findRevisions(CustomDateRevEntity.class,
+				revisionNumbers);
+
+		assert revisions.size() == 5;
+	}
+
+	@Test(enabled = true)
+	public void testRevisionsCounts() {
+		assert Arrays.asList(1, 2, 3, 4).equals(
+				getAuditReader().getRevisions(ParentEntity.class, p1_id));
+		assert Arrays.asList(1, 2, 3, 4).equals(
+				getAuditReader().getRevisions(ParentEntity.class, p2_id));
+
+		assert Arrays.asList(1).equals(
+				getAuditReader().getRevisions(Child1Entity.class, c1_1_id));
+		assert Arrays.asList(1, 5).equals(
+				getAuditReader().getRevisions(Child1Entity.class, c1_2_id));
+
+		assert Arrays.asList(1).equals(
+				getAuditReader().getRevisions(Child2Entity.class, c2_1_id));
+		assert Arrays.asList(1, 5).equals(
+				getAuditReader().getRevisions(Child2Entity.class, c2_2_id));
+	}
+
+	@Test(enabled = true)
+	public void testAllRevEndTimeStamps() {
+		List<Map<String, Object>> p1RevList = getRevisions(ParentEntity.class,
+				p1_id);
+		List<Map<String, Object>> p2RevList = getRevisions(ParentEntity.class,
+				p2_id);
+		List<Map<String, Object>> c1_1_List = getRevisions(Child1Entity.class,
+				c1_1_id);
+		List<Map<String, Object>> c1_2_List = getRevisions(Child1Entity.class,
+				c1_2_id);
+		List<Map<String, Object>> c2_1_List = getRevisions(Child2Entity.class,
+				c2_1_id);
+		List<Map<String, Object>> c2_2_List = getRevisions(Child2Entity.class,
+				c2_2_id);
+
+		verifyRevEndTimeStamps("ParentEntity: " + p1_id, p1RevList);
+		verifyRevEndTimeStamps("ParentEntity: " + p2_id, p2RevList);
+		verifyRevEndTimeStamps("Child1Entity: " + c1_1_id, c1_1_List);
+		verifyRevEndTimeStamps("Child1Entity: " + c1_2_id, c1_2_List);
+		verifyRevEndTimeStamps("Child2Entity: " + c2_1_id, c2_1_List);
+		verifyRevEndTimeStamps("Child2Entity: " + c2_2_id, c2_2_List);
+
+	}
+
+	@Test(enabled = true)
+	public void testHistoryOfParent1() {
+
+		Child1Entity c1_1 = getEntityManager()
+				.find(Child1Entity.class, c1_1_id);
+		Child1Entity c1_2 = getEntityManager()
+				.find(Child1Entity.class, c1_2_id);
+		Child2Entity c2_2 = getEntityManager()
+				.find(Child2Entity.class, c2_2_id);
+
+		ParentEntity rev1 = getAuditReader().find(ParentEntity.class, p1_id, 1);
+		ParentEntity rev2 = getAuditReader().find(ParentEntity.class, p1_id, 2);
+		ParentEntity rev3 = getAuditReader().find(ParentEntity.class, p1_id, 3);
+		ParentEntity rev4 = getAuditReader().find(ParentEntity.class, p1_id, 4);
+		ParentEntity rev5 = getAuditReader().find(ParentEntity.class, p1_id, 5);
+
+		assert TestTools.checkList(rev1.getChildren1());
+		assert TestTools.checkList(rev2.getChildren1(), c1_1);
+		assert TestTools.checkList(rev3.getChildren1(), c1_1, c1_2);
+		assert TestTools.checkList(rev4.getChildren1(), c1_2);
+		assert TestTools.checkList(rev5.getChildren1());
+
+		assert TestTools.checkList(rev1.getChildren2());
+		assert TestTools.checkList(rev2.getChildren2());
+		assert TestTools.checkList(rev3.getChildren2(), c2_2);
+		assert TestTools.checkList(rev4.getChildren2(), c2_2);
+		assert TestTools.checkList(rev5.getChildren2(), c2_2);
+	}
+
+	@Test(enabled = true)
+	public void testHistoryOfParent2() {
+		Child1Entity c1_1 = getEntityManager()
+				.find(Child1Entity.class, c1_1_id);
+		Child2Entity c2_1 = getEntityManager()
+				.find(Child2Entity.class, c2_1_id);
+		Child2Entity c2_2 = getEntityManager()
+				.find(Child2Entity.class, c2_2_id);
+
+		ParentEntity rev1 = getAuditReader().find(ParentEntity.class, p2_id, 1);
+		ParentEntity rev2 = getAuditReader().find(ParentEntity.class, p2_id, 2);
+		ParentEntity rev3 = getAuditReader().find(ParentEntity.class, p2_id, 3);
+		ParentEntity rev4 = getAuditReader().find(ParentEntity.class, p2_id, 4);
+		ParentEntity rev5 = getAuditReader().find(ParentEntity.class, p2_id, 5);
+
+		assert TestTools.checkList(rev1.getChildren1());
+		assert TestTools.checkList(rev2.getChildren1());
+		assert TestTools.checkList(rev3.getChildren1(), c1_1);
+		assert TestTools.checkList(rev4.getChildren1(), c1_1);
+		assert TestTools.checkList(rev5.getChildren1(), c1_1);
+
+		assert TestTools.checkList(rev1.getChildren2());
+		assert TestTools.checkList(rev2.getChildren2(), c2_1);
+		assert TestTools.checkList(rev3.getChildren2(), c2_1);
+		assert TestTools.checkList(rev4.getChildren2(), c2_1, c2_2);
+		assert TestTools.checkList(rev5.getChildren2(), c2_1);
+	}
+
+	@Test(enabled = true)
+	public void testHistoryOfChild1_1() {
+		ParentEntity p1 = getEntityManager().find(ParentEntity.class, p1_id);
+		ParentEntity p2 = getEntityManager().find(ParentEntity.class, p2_id);
+
+		Child1Entity rev1 = getAuditReader().find(Child1Entity.class, c1_1_id,
+				1);
+		Child1Entity rev2 = getAuditReader().find(Child1Entity.class, c1_1_id,
+				2);
+		Child1Entity rev3 = getAuditReader().find(Child1Entity.class, c1_1_id,
+				3);
+		Child1Entity rev4 = getAuditReader().find(Child1Entity.class, c1_1_id,
+				4);
+		Child1Entity rev5 = getAuditReader().find(Child1Entity.class, c1_1_id,
+				5);
+
+		assert TestTools.checkList(rev1.getParents());
+		assert TestTools.checkList(rev2.getParents(), p1);
+		assert TestTools.checkList(rev3.getParents(), p1, p2);
+		assert TestTools.checkList(rev4.getParents(), p2);
+		assert TestTools.checkList(rev5.getParents(), p2);
+	}
+
+	@Test(enabled = false)
+	public void testHistoryOfChild1_2() {
+		ParentEntity p1 = getEntityManager().find(ParentEntity.class, p1_id);
+
+		Child1Entity rev1 = getAuditReader().find(Child1Entity.class, c1_2_id,
+				1);
+		Child1Entity rev2 = getAuditReader().find(Child1Entity.class, c1_2_id,
+				2);
+		Child1Entity rev3 = getAuditReader().find(Child1Entity.class, c1_2_id,
+				3);
+		Child1Entity rev4 = getAuditReader().find(Child1Entity.class, c1_2_id,
+				4);
+		Child1Entity rev5 = getAuditReader().find(Child1Entity.class, c1_2_id,
+				5);
+
+		assert TestTools.checkList(rev1.getParents());
+		assert TestTools.checkList(rev2.getParents());
+		assert TestTools.checkList(rev3.getParents(), p1);
+		assert TestTools.checkList(rev4.getParents(), p1);
+		assert TestTools.checkList(rev5.getParents());
+	}
+
+	@Test(enabled = true)
+	public void testHistoryOfChild2_1() {
+		ParentEntity p2 = getEntityManager().find(ParentEntity.class, p2_id);
+
+		Child2Entity rev1 = getAuditReader().find(Child2Entity.class, c2_1_id,
+				1);
+		Child2Entity rev2 = getAuditReader().find(Child2Entity.class, c2_1_id,
+				2);
+		Child2Entity rev3 = getAuditReader().find(Child2Entity.class, c2_1_id,
+				3);
+		Child2Entity rev4 = getAuditReader().find(Child2Entity.class, c2_1_id,
+				4);
+		Child2Entity rev5 = getAuditReader().find(Child2Entity.class, c2_1_id,
+				5);
+
+		assert TestTools.checkList(rev1.getParents());
+		assert TestTools.checkList(rev2.getParents(), p2);
+		assert TestTools.checkList(rev3.getParents(), p2);
+		assert TestTools.checkList(rev4.getParents(), p2);
+		assert TestTools.checkList(rev5.getParents(), p2);
+	}
+
+	@Test(enabled = true)
+	public void testHistoryOfChild2_2() {
+		ParentEntity p1 = getEntityManager().find(ParentEntity.class, p1_id);
+		ParentEntity p2 = getEntityManager().find(ParentEntity.class, p2_id);
+
+		Child2Entity rev1 = getAuditReader().find(Child2Entity.class, c2_2_id,
+				1);
+		Child2Entity rev2 = getAuditReader().find(Child2Entity.class, c2_2_id,
+				2);
+		Child2Entity rev3 = getAuditReader().find(Child2Entity.class, c2_2_id,
+				3);
+		Child2Entity rev4 = getAuditReader().find(Child2Entity.class, c2_2_id,
+				4);
+		Child2Entity rev5 = getAuditReader().find(Child2Entity.class, c2_2_id,
+				5);
+
+		assert TestTools.checkList(rev1.getParents());
+		assert TestTools.checkList(rev2.getParents());
+		assert TestTools.checkList(rev3.getParents(), p1);
+		assert TestTools.checkList(rev4.getParents(), p1, p2);
+		assert TestTools.checkList(rev5.getParents(), p1);
+	}
+
+	private List<Map<String, Object>> getRevisions(
+			Class<?> originalEntityClazz, Integer originalEntityId) {
+		// Build the query:
+		// select auditEntity from
+		// org.hibernate.envers.test.entities.manytomany.sametable.ParentEntity_AUD
+		// auditEntity where auditEntity.originalId.id = :originalEntityId
+
+		StringBuilder builder = new StringBuilder("select auditEntity from ");
+		builder.append(originalEntityClazz.getName())
+				.append("_AUD auditEntity");
+		builder.append(" where auditEntity.originalId.id = :originalEntityId");
+
+		Query qry = getEntityManager().createQuery(builder.toString());
+		qry.setParameter("originalEntityId", originalEntityId);
+
+		@SuppressWarnings("unchecked")
+		List<Map<String, Object>> resultList = qry.getResultList();
+		return resultList;
+	}
+
+	private void verifyRevEndTimeStamps(String debugInfo,
+			List<Map<String, Object>> revisionEntities) {
+		for (Map<String, Object> revisionEntity : revisionEntities) {
+
+			Date revendTimestamp = (Date) revisionEntity
+					.get(revendTimestampColumName);
+			CustomDateRevEntity revEnd = (CustomDateRevEntity) revisionEntity
+					.get("REVEND");
+
+			if (revendTimestamp == null) {
+				assert revEnd == null;
+			} else {
+				assert revendTimestamp.getTime() == revEnd.getDateTimestamp().getTime();
+			}
+		}
+	}
+
+}

Added: core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTsTest.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTsTest.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTsTest.java	2010-09-28 22:23:04 UTC (rev 20743)
@@ -0,0 +1,432 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.envers.test.integration.strategy;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+
+import org.hibernate.Session;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.envers.DefaultRevisionEntity;
+import org.hibernate.envers.strategy.ValidityAuditStrategy;
+import org.hibernate.envers.test.AbstractEntityTest;
+import org.hibernate.envers.test.entities.manytomany.sametable.Child1Entity;
+import org.hibernate.envers.test.entities.manytomany.sametable.Child2Entity;
+import org.hibernate.envers.test.entities.manytomany.sametable.ParentEntity;
+import org.hibernate.envers.test.tools.TestTools;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ * Test which checks that the revision end timestamp is correctly set for
+ * {@link ValidityAuditStrategy}.
+ * 
+ * @author Erik-Berndt Scheper
+ */
+public class ValidityAuditStrategyRevEndTsTest extends AbstractEntityTest {
+	private final String revendTimestampColumName = "REVEND_TIMESTAMP";
+
+	private Integer p1_id;
+	private Integer p2_id;
+	private Integer c1_1_id;
+	private Integer c1_2_id;
+	private Integer c2_1_id;
+	private Integer c2_2_id;
+	private Map<Number, DefaultRevisionEntity> revisions;
+
+	public void configure(Ejb3Configuration cfg) {
+		cfg.addAnnotatedClass(ParentEntity.class);
+		cfg.addAnnotatedClass(Child1Entity.class);
+		cfg.addAnnotatedClass(Child2Entity.class);
+
+		cfg.setProperty("org.hibernate.envers.audit_strategy",
+				"org.hibernate.envers.strategy.ValidityAuditStrategy");
+		cfg
+				.setProperty(
+						"org.hibernate.envers.audit_strategy_validity_store_revend_timestamp",
+						"true");
+		cfg
+				.setProperty(
+						"org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name",
+						revendTimestampColumName);
+	}
+
+	@BeforeClass(enabled = true, dependsOnMethods = "init")
+	public void initData() {
+		EntityManager em = getEntityManager();
+
+		// We need first to modify the columns in the middle (join table) to
+		// allow null values. Hbm2ddl doesn't seem
+		// to allow this.
+		em.getTransaction().begin();
+		Session session = (Session) em.getDelegate();
+		session.createSQLQuery("DROP TABLE children").executeUpdate();
+		session
+				.createSQLQuery(
+						"CREATE TABLE children(parent_id integer, child1_id integer NULL, child2_id integer NULL)")
+				.executeUpdate();
+		session.createSQLQuery("DROP TABLE children_AUD").executeUpdate();
+		session
+				.createSQLQuery(
+						"CREATE TABLE children_AUD(REV integer NOT NULL, REVEND integer, "
+								+ revendTimestampColumName
+								+ " timestamp, REVTYPE tinyint, "
+								+ "parent_id integer, child1_id integer NULL, child2_id integer NULL)")
+				.executeUpdate();
+		em.getTransaction().commit();
+		em.clear();
+
+		ParentEntity p1 = new ParentEntity("parent_1");
+		ParentEntity p2 = new ParentEntity("parent_2");
+
+		Child1Entity c1_1 = new Child1Entity("child1_1");
+		Child1Entity c1_2 = new Child1Entity("child1_2");
+
+		Child2Entity c2_1 = new Child2Entity("child2_1");
+		Child2Entity c2_2 = new Child2Entity("child2_2");
+
+		// Revision 1
+		em.getTransaction().begin();
+
+		em.persist(p1);
+		em.persist(p2);
+		em.persist(c1_1);
+		em.persist(c1_2);
+		em.persist(c2_1);
+		em.persist(c2_2);
+
+		em.getTransaction().commit();
+		em.clear();
+
+		// Revision 2 - (p1: c1_1, p2: c2_1)
+
+		em.getTransaction().begin();
+
+		p1 = em.find(ParentEntity.class, p1.getId());
+		p2 = em.find(ParentEntity.class, p2.getId());
+		c1_1 = em.find(Child1Entity.class, c1_1.getId());
+		c2_1 = em.find(Child2Entity.class, c2_1.getId());
+
+		p1.getChildren1().add(c1_1);
+		p2.getChildren2().add(c2_1);
+
+		em.getTransaction().commit();
+		em.clear();
+
+		// Revision 3 - (p1: c1_1, c1_2, c2_2, p2: c1_1, c2_1)
+		em.getTransaction().begin();
+
+		p1 = em.find(ParentEntity.class, p1.getId());
+		p2 = em.find(ParentEntity.class, p2.getId());
+		c1_1 = em.find(Child1Entity.class, c1_1.getId());
+		c1_2 = em.find(Child1Entity.class, c1_2.getId());
+		c2_2 = em.find(Child2Entity.class, c2_2.getId());
+
+		p1.getChildren1().add(c1_2);
+		p1.getChildren2().add(c2_2);
+
+		p2.getChildren1().add(c1_1);
+
+		em.getTransaction().commit();
+		em.clear();
+
+		// Revision 4 - (p1: c1_2, c2_2, p2: c1_1, c2_1, c2_2)
+		em.getTransaction().begin();
+
+		p1 = em.find(ParentEntity.class, p1.getId());
+		p2 = em.find(ParentEntity.class, p2.getId());
+		c1_1 = em.find(Child1Entity.class, c1_1.getId());
+		c2_2 = em.find(Child2Entity.class, c2_2.getId());
+
+		p1.getChildren1().remove(c1_1);
+		p2.getChildren2().add(c2_2);
+
+		em.getTransaction().commit();
+		em.clear();
+
+		// Revision 5 - (p1: c2_2, p2: c1_1, c2_1)
+		em.getTransaction().begin();
+
+		p1 = em.find(ParentEntity.class, p1.getId());
+		p2 = em.find(ParentEntity.class, p2.getId());
+		c1_2 = em.find(Child1Entity.class, c1_2.getId());
+		c2_2 = em.find(Child2Entity.class, c2_2.getId());
+
+		c2_2.getParents().remove(p2);
+		c1_2.getParents().remove(p1);
+
+		em.getTransaction().commit();
+		em.clear();
+
+		//
+
+		p1_id = p1.getId();
+		p2_id = p2.getId();
+		c1_1_id = c1_1.getId();
+		c1_2_id = c1_2.getId();
+		c2_1_id = c2_1.getId();
+		c2_2_id = c2_2.getId();
+
+		Set<Number> revisionNumbers = new HashSet<Number>();
+		revisionNumbers.addAll(Arrays.asList(1, 2, 3, 4, 5));
+		revisions = getAuditReader().findRevisions(DefaultRevisionEntity.class,
+				revisionNumbers);
+
+		assert revisions.size() == 5;
+	}
+
+	@Test(enabled = true)
+	public void testRevisionsCounts() {
+		assert Arrays.asList(1, 2, 3, 4).equals(
+				getAuditReader().getRevisions(ParentEntity.class, p1_id));
+		assert Arrays.asList(1, 2, 3, 4).equals(
+				getAuditReader().getRevisions(ParentEntity.class, p2_id));
+
+		assert Arrays.asList(1).equals(
+				getAuditReader().getRevisions(Child1Entity.class, c1_1_id));
+		assert Arrays.asList(1, 5).equals(
+				getAuditReader().getRevisions(Child1Entity.class, c1_2_id));
+
+		assert Arrays.asList(1).equals(
+				getAuditReader().getRevisions(Child2Entity.class, c2_1_id));
+		assert Arrays.asList(1, 5).equals(
+				getAuditReader().getRevisions(Child2Entity.class, c2_2_id));
+	}
+
+	@Test(enabled = true)
+	public void testAllRevEndTimeStamps() {
+		List<Map<String, Object>> p1RevList = getRevisions(ParentEntity.class,
+				p1_id);
+		List<Map<String, Object>> p2RevList = getRevisions(ParentEntity.class,
+				p2_id);
+		List<Map<String, Object>> c1_1_List = getRevisions(Child1Entity.class,
+				c1_1_id);
+		List<Map<String, Object>> c1_2_List = getRevisions(Child1Entity.class,
+				c1_2_id);
+		List<Map<String, Object>> c2_1_List = getRevisions(Child2Entity.class,
+				c2_1_id);
+		List<Map<String, Object>> c2_2_List = getRevisions(Child2Entity.class,
+				c2_2_id);
+
+		verifyRevEndTimeStamps("ParentEntity: " + p1_id, p1RevList);
+		verifyRevEndTimeStamps("ParentEntity: " + p2_id, p2RevList);
+		verifyRevEndTimeStamps("Child1Entity: " + c1_1_id, c1_1_List);
+		verifyRevEndTimeStamps("Child1Entity: " + c1_2_id, c1_2_List);
+		verifyRevEndTimeStamps("Child2Entity: " + c2_1_id, c2_1_List);
+		verifyRevEndTimeStamps("Child2Entity: " + c2_2_id, c2_2_List);
+
+	}
+
+	@Test(enabled = true)
+	public void testHistoryOfParent1() {
+
+		Child1Entity c1_1 = getEntityManager()
+				.find(Child1Entity.class, c1_1_id);
+		Child1Entity c1_2 = getEntityManager()
+				.find(Child1Entity.class, c1_2_id);
+		Child2Entity c2_2 = getEntityManager()
+				.find(Child2Entity.class, c2_2_id);
+
+		ParentEntity rev1 = getAuditReader().find(ParentEntity.class, p1_id, 1);
+		ParentEntity rev2 = getAuditReader().find(ParentEntity.class, p1_id, 2);
+		ParentEntity rev3 = getAuditReader().find(ParentEntity.class, p1_id, 3);
+		ParentEntity rev4 = getAuditReader().find(ParentEntity.class, p1_id, 4);
+		ParentEntity rev5 = getAuditReader().find(ParentEntity.class, p1_id, 5);
+
+		assert TestTools.checkList(rev1.getChildren1());
+		assert TestTools.checkList(rev2.getChildren1(), c1_1);
+		assert TestTools.checkList(rev3.getChildren1(), c1_1, c1_2);
+		assert TestTools.checkList(rev4.getChildren1(), c1_2);
+		assert TestTools.checkList(rev5.getChildren1());
+
+		assert TestTools.checkList(rev1.getChildren2());
+		assert TestTools.checkList(rev2.getChildren2());
+		assert TestTools.checkList(rev3.getChildren2(), c2_2);
+		assert TestTools.checkList(rev4.getChildren2(), c2_2);
+		assert TestTools.checkList(rev5.getChildren2(), c2_2);
+	}
+
+	@Test(enabled = true)
+	public void testHistoryOfParent2() {
+		Child1Entity c1_1 = getEntityManager()
+				.find(Child1Entity.class, c1_1_id);
+		Child2Entity c2_1 = getEntityManager()
+				.find(Child2Entity.class, c2_1_id);
+		Child2Entity c2_2 = getEntityManager()
+				.find(Child2Entity.class, c2_2_id);
+
+		ParentEntity rev1 = getAuditReader().find(ParentEntity.class, p2_id, 1);
+		ParentEntity rev2 = getAuditReader().find(ParentEntity.class, p2_id, 2);
+		ParentEntity rev3 = getAuditReader().find(ParentEntity.class, p2_id, 3);
+		ParentEntity rev4 = getAuditReader().find(ParentEntity.class, p2_id, 4);
+		ParentEntity rev5 = getAuditReader().find(ParentEntity.class, p2_id, 5);
+
+		assert TestTools.checkList(rev1.getChildren1());
+		assert TestTools.checkList(rev2.getChildren1());
+		assert TestTools.checkList(rev3.getChildren1(), c1_1);
+		assert TestTools.checkList(rev4.getChildren1(), c1_1);
+		assert TestTools.checkList(rev5.getChildren1(), c1_1);
+
+		assert TestTools.checkList(rev1.getChildren2());
+		assert TestTools.checkList(rev2.getChildren2(), c2_1);
+		assert TestTools.checkList(rev3.getChildren2(), c2_1);
+		assert TestTools.checkList(rev4.getChildren2(), c2_1, c2_2);
+		assert TestTools.checkList(rev5.getChildren2(), c2_1);
+	}
+
+	@Test(enabled = true)
+	public void testHistoryOfChild1_1() {
+		ParentEntity p1 = getEntityManager().find(ParentEntity.class, p1_id);
+		ParentEntity p2 = getEntityManager().find(ParentEntity.class, p2_id);
+
+		Child1Entity rev1 = getAuditReader().find(Child1Entity.class, c1_1_id,
+				1);
+		Child1Entity rev2 = getAuditReader().find(Child1Entity.class, c1_1_id,
+				2);
+		Child1Entity rev3 = getAuditReader().find(Child1Entity.class, c1_1_id,
+				3);
+		Child1Entity rev4 = getAuditReader().find(Child1Entity.class, c1_1_id,
+				4);
+		Child1Entity rev5 = getAuditReader().find(Child1Entity.class, c1_1_id,
+				5);
+
+		assert TestTools.checkList(rev1.getParents());
+		assert TestTools.checkList(rev2.getParents(), p1);
+		assert TestTools.checkList(rev3.getParents(), p1, p2);
+		assert TestTools.checkList(rev4.getParents(), p2);
+		assert TestTools.checkList(rev5.getParents(), p2);
+	}
+
+	@Test(enabled = false)
+	public void testHistoryOfChild1_2() {
+		ParentEntity p1 = getEntityManager().find(ParentEntity.class, p1_id);
+
+		Child1Entity rev1 = getAuditReader().find(Child1Entity.class, c1_2_id,
+				1);
+		Child1Entity rev2 = getAuditReader().find(Child1Entity.class, c1_2_id,
+				2);
+		Child1Entity rev3 = getAuditReader().find(Child1Entity.class, c1_2_id,
+				3);
+		Child1Entity rev4 = getAuditReader().find(Child1Entity.class, c1_2_id,
+				4);
+		Child1Entity rev5 = getAuditReader().find(Child1Entity.class, c1_2_id,
+				5);
+
+		assert TestTools.checkList(rev1.getParents());
+		assert TestTools.checkList(rev2.getParents());
+		assert TestTools.checkList(rev3.getParents(), p1);
+		assert TestTools.checkList(rev4.getParents(), p1);
+		assert TestTools.checkList(rev5.getParents());
+	}
+
+	@Test(enabled = true)
+	public void testHistoryOfChild2_1() {
+		ParentEntity p2 = getEntityManager().find(ParentEntity.class, p2_id);
+
+		Child2Entity rev1 = getAuditReader().find(Child2Entity.class, c2_1_id,
+				1);
+		Child2Entity rev2 = getAuditReader().find(Child2Entity.class, c2_1_id,
+				2);
+		Child2Entity rev3 = getAuditReader().find(Child2Entity.class, c2_1_id,
+				3);
+		Child2Entity rev4 = getAuditReader().find(Child2Entity.class, c2_1_id,
+				4);
+		Child2Entity rev5 = getAuditReader().find(Child2Entity.class, c2_1_id,
+				5);
+
+		assert TestTools.checkList(rev1.getParents());
+		assert TestTools.checkList(rev2.getParents(), p2);
+		assert TestTools.checkList(rev3.getParents(), p2);
+		assert TestTools.checkList(rev4.getParents(), p2);
+		assert TestTools.checkList(rev5.getParents(), p2);
+	}
+
+	@Test(enabled = true)
+	public void testHistoryOfChild2_2() {
+		ParentEntity p1 = getEntityManager().find(ParentEntity.class, p1_id);
+		ParentEntity p2 = getEntityManager().find(ParentEntity.class, p2_id);
+
+		Child2Entity rev1 = getAuditReader().find(Child2Entity.class, c2_2_id,
+				1);
+		Child2Entity rev2 = getAuditReader().find(Child2Entity.class, c2_2_id,
+				2);
+		Child2Entity rev3 = getAuditReader().find(Child2Entity.class, c2_2_id,
+				3);
+		Child2Entity rev4 = getAuditReader().find(Child2Entity.class, c2_2_id,
+				4);
+		Child2Entity rev5 = getAuditReader().find(Child2Entity.class, c2_2_id,
+				5);
+
+		assert TestTools.checkList(rev1.getParents());
+		assert TestTools.checkList(rev2.getParents());
+		assert TestTools.checkList(rev3.getParents(), p1);
+		assert TestTools.checkList(rev4.getParents(), p1, p2);
+		assert TestTools.checkList(rev5.getParents(), p1);
+	}
+
+	private List<Map<String, Object>> getRevisions(
+			Class<?> originalEntityClazz, Integer originalEntityId) {
+		// Build the query:
+		// select auditEntity from
+		// org.hibernate.envers.test.entities.manytomany.sametable.ParentEntity_AUD
+		// auditEntity where auditEntity.originalId.id = :originalEntityId
+
+		StringBuilder builder = new StringBuilder("select auditEntity from ");
+		builder.append(originalEntityClazz.getName())
+				.append("_AUD auditEntity");
+		builder.append(" where auditEntity.originalId.id = :originalEntityId");
+
+		Query qry = getEntityManager().createQuery(builder.toString());
+		qry.setParameter("originalEntityId", originalEntityId);
+
+		@SuppressWarnings("unchecked")
+		List<Map<String, Object>> resultList = qry.getResultList();
+		return resultList;
+	}
+
+	private void verifyRevEndTimeStamps(String debugInfo,
+			List<Map<String, Object>> revisionEntities) {
+		for (Map<String, Object> revisionEntity : revisionEntities) {
+
+			Date revendTimestamp = (Date) revisionEntity
+					.get(revendTimestampColumName);
+			DefaultRevisionEntity revEnd = (DefaultRevisionEntity) revisionEntity
+					.get("REVEND");
+
+			if (revendTimestamp == null) {
+				assert revEnd == null;
+			} else {
+				assert revendTimestamp.getTime() == revEnd.getTimestamp();
+			}
+		}
+	}
+
+}

Modified: core/trunk/envers/src/test/resources/testng.xml
===================================================================
--- core/trunk/envers/src/test/resources/testng.xml	2010-09-28 16:39:06 UTC (rev 20742)
+++ core/trunk/envers/src/test/resources/testng.xml	2010-09-28 22:23:04 UTC (rev 20743)
@@ -68,6 +68,7 @@
             <package name="org.hibernate.envers.test.integration.secondary" />
             <package name="org.hibernate.envers.test.integration.secondary.ids" />
             <package name="org.hibernate.envers.test.integration.serialization" />
+            <package name="org.hibernate.envers.test.integration.strategy" />
             <package name="org.hibernate.envers.test.integration.superclass" /> 
             <package name="org.hibernate.envers.test.integration.entityNames.auditedEntity" />
             <package name="org.hibernate.envers.test.integration.entityNames.manyToManyAudited" />



More information about the hibernate-commits mailing list