diff --git a/documentation/envers/src/main/docbook/en-US/content/configuration.xml b/documentation/envers/src/main/docbook/en-US/content/configuration.xml index 1b050c230d..eac3afa3e5 100644 --- a/documentation/envers/src/main/docbook/en-US/content/configuration.xml +++ b/documentation/envers/src/main/docbook/en-US/content/configuration.xml @@ -218,4 +218,11 @@ please see for a description of the additional @AuditJoinTable annotation that you'll probably want to use. + + + If you want to audit a relation, where the target entity is not audited (that is the case for example with + dictionary-like entities, which don't change and don't have to be audited), just annotate it with + @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED). Then, when reading historic + versions of your entity, the relation will always point to the "current" related entity. + diff --git a/envers/src/main/java/org/hibernate/envers/Audited.java b/envers/src/main/java/org/hibernate/envers/Audited.java index b665677b11..01dc2134c1 100644 --- a/envers/src/main/java/org/hibernate/envers/Audited.java +++ b/envers/src/main/java/org/hibernate/envers/Audited.java @@ -32,9 +32,17 @@ import java.lang.annotation.Target; * When applied to a class, indicates that all of its properties should be audited. * When applied to a field, indicates that this field should be audited. * @author Adam Warski (adam at warski dot org) + * @author Tomasz Bech */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) public @interface Audited { ModificationStore modStore() default ModificationStore.FULL; + + /** + * @return Specifies if the entity that is the target of the relation should be audited or not. If not, then when + * reading a historic version an audited entity, the realtion will always point to the "current" entity. + * This is useful for dictionary-like entities, which don't change and don't need to be audited. + */ + RelationTargetAuditMode targetAuditMode() default RelationTargetAuditMode.AUDITED; } diff --git a/envers/src/main/java/org/hibernate/envers/RelationTargetAuditMode.java b/envers/src/main/java/org/hibernate/envers/RelationTargetAuditMode.java new file mode 100644 index 0000000000..b4216f7298 --- /dev/null +++ b/envers/src/main/java/org/hibernate/envers/RelationTargetAuditMode.java @@ -0,0 +1,32 @@ +/* + * 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; + +/** + * @author Tomasz Bech + */ +public enum RelationTargetAuditMode { + AUDITED, + NOT_AUDITED +} diff --git a/envers/src/main/java/org/hibernate/envers/configuration/EntitiesConfigurator.java b/envers/src/main/java/org/hibernate/envers/configuration/EntitiesConfigurator.java index b858e9807a..47539b1bda 100644 --- a/envers/src/main/java/org/hibernate/envers/configuration/EntitiesConfigurator.java +++ b/envers/src/main/java/org/hibernate/envers/configuration/EntitiesConfigurator.java @@ -84,9 +84,13 @@ public class EntitiesConfigurator { } EntityXmlMappingData xmlMappingData = new EntityXmlMappingData(); - auditMetaGen.generateFirstPass(pc, auditData, xmlMappingData); + auditMetaGen.generateFirstPass(pc, auditData, xmlMappingData, true); xmlMappings.put(pc, xmlMappingData); - } + } else { + EntityXmlMappingData xmlMappingData = new EntityXmlMappingData(); + auditMetaGen.generateFirstPass(pc, auditData, xmlMappingData, false); + xmlMappings.put(pc, xmlMappingData); + } } // Second pass @@ -123,7 +127,8 @@ public class EntitiesConfigurator { } } - return new EntitiesConfigurations(auditMetaGen.getEntitiesConfigurations()); + return new EntitiesConfigurations(auditMetaGen.getEntitiesConfigurations(), + auditMetaGen.getNotAuditedEntitiesConfigurations()); } // todo diff --git a/envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java b/envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java index 4761f1a328..762f82431e 100644 --- a/envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java +++ b/envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java @@ -53,6 +53,7 @@ import org.hibernate.type.*; /** * @author Adam Warski (adam at warski dot org) * @author Sebastian Komander + * @author Tomasz Bech */ public final class AuditMetadataGenerator { private final Configuration cfg; @@ -66,6 +67,7 @@ public final class AuditMetadataGenerator { private final ToOneRelationMetadataGenerator toOneRelationMetadataGenerator; private final Map entitiesConfigurations; + private final Map notAuditedEntitiesConfigurations; // Map entity name -> (join descriptor -> element describing the "versioned" join) private final Map> entitiesJoins; @@ -84,6 +86,7 @@ public final class AuditMetadataGenerator { this.toOneRelationMetadataGenerator = new ToOneRelationMetadataGenerator(this); entitiesConfigurations = new HashMap(); + notAuditedEntitiesConfigurations = new HashMap(); entitiesJoins = new HashMap>(); } @@ -278,7 +281,7 @@ public final class AuditMetadataGenerator { @SuppressWarnings({"unchecked"}) public void generateFirstPass(PersistentClass pc, ClassAuditingData auditingData, - EntityXmlMappingData xmlMappingData) { + EntityXmlMappingData xmlMappingData, boolean isAudited) { String schema = auditingData.getAuditTable().schema(); if (StringTools.isEmpty(schema)) { schema = pc.getTable().getSchema(); @@ -289,6 +292,17 @@ public final class AuditMetadataGenerator { catalog = pc.getTable().getCatalog(); } + if (!isAudited) { + String entityName = pc.getEntityName(); + IdMappingData idMapper = idMetadataGenerator.addId(pc); + ExtendedPropertyMapper propertyMapper = null; + String parentEntityName = null; + EntityConfiguration entityCfg = new EntityConfiguration(entityName, idMapper, propertyMapper, + parentEntityName); + notAuditedEntitiesConfigurations.put(pc.getEntityName(), entityCfg); + return; + } + String entityName = pc.getEntityName(); String auditEntityName = verEntCfg.getAuditEntityName(entityName); String auditTableName = verEntCfg.getAuditTableName(entityName, pc.getTable().getName()); @@ -400,4 +414,13 @@ public final class AuditMetadataGenerator { throw new MappingException(message); } + + /** + * Get the notAuditedEntitiesConfigurations property. + * + * @return the notAuditedEntitiesConfigurations property value + */ + public Map getNotAuditedEntitiesConfigurations() { + return notAuditedEntitiesConfigurations; + } } diff --git a/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java b/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java index 1c7a987e43..d645fa2a20 100644 --- a/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java +++ b/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java @@ -36,6 +36,7 @@ import javax.persistence.JoinColumn; import org.dom4j.Element; import org.hibernate.envers.ModificationStore; +import org.hibernate.envers.RelationTargetAuditMode; import org.hibernate.envers.configuration.metadata.reader.PropertyAuditingData; import org.hibernate.envers.entities.EntityConfiguration; import org.hibernate.envers.entities.IdMappingData; @@ -231,7 +232,7 @@ public final class CollectionMetadataGenerator { // middle table for mapping this relation. return StringTools.getLastComponent(entityName) + "_" + StringTools.getLastComponent(getReferencedEntityName(value.getElement())); } else { - // Hibernate uses a middle table for mapping this relation, so we get it's name directly. + // Hibernate uses a middle table for mapping this relation, so we get it's name directly. return value.getCollectionTable().getName(); } } @@ -413,7 +414,7 @@ public final class CollectionMetadataGenerator { } else { // Last but one parameter: collection components are always insertable boolean mapped = mainGenerator.getBasicMetadataGenerator().addBasic(xmlMapping, - new PropertyAuditingData(prefix, "field", ModificationStore.FULL), value, null, + new PropertyAuditingData(prefix, "field", ModificationStore.FULL, RelationTargetAuditMode.AUDITED), value, null, true, true); if (mapped) { diff --git a/envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java b/envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java index 11583e6182..1b68c47790 100644 --- a/envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java +++ b/envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java @@ -28,6 +28,7 @@ import java.util.Iterator; import org.dom4j.Element; import org.dom4j.tree.DefaultElement; import org.hibernate.envers.ModificationStore; +import org.hibernate.envers.RelationTargetAuditMode; import org.hibernate.envers.configuration.metadata.reader.PropertyAuditingData; import org.hibernate.envers.entities.IdMappingData; import org.hibernate.envers.entities.PropertyData; @@ -133,6 +134,6 @@ public final class IdMetadataGenerator { private PropertyAuditingData getIdPersistentPropertyAuditingData(Property property) { return new PropertyAuditingData(property.getName(), property.getPropertyAccessorName(), - ModificationStore.FULL); + ModificationStore.FULL, RelationTargetAuditMode.AUDITED); } } diff --git a/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java b/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java index 7cafc3a87a..e323477390 100644 --- a/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java +++ b/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java @@ -24,6 +24,7 @@ package org.hibernate.envers.configuration.metadata; import org.dom4j.Element; +import org.hibernate.envers.RelationTargetAuditMode; import org.hibernate.envers.entities.EntityConfiguration; import org.hibernate.envers.entities.IdMappingData; import org.hibernate.envers.entities.PropertyData; @@ -54,10 +55,22 @@ public final class ToOneRelationMetadataGenerator { CompositeMapperBuilder mapper, String entityName, boolean insertable) { String referencedEntityName = ((ToOne) value).getReferencedEntityName(); - EntityConfiguration configuration = mainGenerator.getEntitiesConfigurations().get(referencedEntityName); - if (configuration == null) { - throw new MappingException("An audited relation to a non-audited entity " + referencedEntityName + "!"); - } + EntityConfiguration configuration = mainGenerator.getEntitiesConfigurations().get(referencedEntityName); + if (configuration == null) { + configuration = mainGenerator.getNotAuditedEntitiesConfigurations().get(referencedEntityName); + if (configuration != null) { + RelationTargetAuditMode relationTargetAuditMode = propertyAuditingData.getRelationTargetAuditMode(); + if (!RelationTargetAuditMode.NOT_AUDITED.equals(relationTargetAuditMode)) { + throw new MappingException("An audited relation from " + entityName + "." + + propertyAuditingData.getName() + " to a not audited entity " + referencedEntityName + "!" + + ". Such mapping is possible, but has to be strictly defined using RelationTargetAuditMode.NOT_AUDITED in @Audited."); + } + } + } + if (configuration == null) { + throw new MappingException("An audited relation from " + entityName + "." + + propertyAuditingData.getName() + " to a not audited entity " + referencedEntityName + "!"); + } IdMappingData idMapping = configuration.getIdMappingData(); diff --git a/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java b/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java index e5d92d1f12..ff6e37051b 100644 --- a/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java +++ b/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java @@ -160,6 +160,7 @@ public class AuditedPropertiesReader { Versioned ver = property.getAnnotation(Versioned.class); if (aud != null) { propertyData.setStore(aud.modStore()); + propertyData.setRelationTargetAuditMode(aud.targetAuditMode()); } else if (ver != null) { propertyData.setStore(ModificationStore.FULL); } else { @@ -203,7 +204,7 @@ public class AuditedPropertiesReader { /*** * Add the {@link org.hibernate.envers.AuditOverride} annotations. - * + * * @param property the property being processed * @param propertyData the Envers auditing data for this property */ @@ -220,13 +221,13 @@ public class AuditedPropertiesReader { /** * Process the {@link org.hibernate.envers.AuditOverride} annotations for this property. - * + * * @param property * the property for which the {@link org.hibernate.envers.AuditOverride} * annotations are being processed * @param propertyData * the Envers auditing data for this property - * @return {@code false} if isAudited() of the override annotation was set to + * @return {@code false} if isAudited() of the override annotation was set to */ private boolean processPropertyAuditingOverrides(XProperty property, PropertyAuditingData propertyData) { // if this property is part of a component, process all override annotations @@ -236,7 +237,7 @@ public class AuditedPropertiesReader { if (property.getName().equals(override.name())) { // the override applies to this property if (!override.isAudited()) { - return false; + return false; } else { if (override.auditJoinTable() != null) { propertyData.setJoinTable(override.auditJoinTable()); @@ -267,7 +268,7 @@ public class AuditedPropertiesReader { } catch (ClassNotFoundException e) { throw new MappingException(e); } - + this.component = component; } diff --git a/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/PropertyAuditingData.java b/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/PropertyAuditingData.java index 449b172ddf..3537e698d8 100644 --- a/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/PropertyAuditingData.java +++ b/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/PropertyAuditingData.java @@ -31,6 +31,7 @@ import org.hibernate.envers.AuditOverride; import org.hibernate.envers.AuditOverrides; import org.hibernate.envers.ModificationStore; import org.hibernate.envers.AuditJoinTable; +import org.hibernate.envers.RelationTargetAuditMode; import org.hibernate.envers.entities.PropertyData; /** @@ -44,15 +45,18 @@ public class PropertyAuditingData { private AuditJoinTable joinTable; private String accessType; private final List auditJoinTableOverrides = new ArrayList(0); + private RelationTargetAuditMode relationTargetAuditMode; - public PropertyAuditingData() { + public PropertyAuditingData() { } - public PropertyAuditingData(String name, String accessType, ModificationStore store) { + public PropertyAuditingData(String name, String accessType, ModificationStore store, + RelationTargetAuditMode relationTargetAuditMode) { this.name = name; this.beanName = name; this.accessType = accessType; this.store = store; + this.relationTargetAuditMode = relationTargetAuditMode; } public String getName() { @@ -108,7 +112,7 @@ public class PropertyAuditingData { } public List getAuditingOverrides() { - return auditJoinTableOverrides; + return auditJoinTableOverrides; } public void addAuditingOverride(AuditOverride annotation) { @@ -135,4 +139,22 @@ public class PropertyAuditingData { } } + /** + * Get the relationTargetAuditMode property. + * + * @return the relationTargetAuditMode property value + */ + public RelationTargetAuditMode getRelationTargetAuditMode() { + return relationTargetAuditMode; + } + + /** + * Set the relationTargetAuditMode property value. + * + * @param relationTargetAuditMode the relationTargetAuditMode to set + */ + public void setRelationTargetAuditMode(RelationTargetAuditMode relationTargetAuditMode) { + this.relationTargetAuditMode = relationTargetAuditMode; + } + } diff --git a/envers/src/main/java/org/hibernate/envers/entities/EntitiesConfigurations.java b/envers/src/main/java/org/hibernate/envers/entities/EntitiesConfigurations.java index 46f95b6880..de660e3aee 100644 --- a/envers/src/main/java/org/hibernate/envers/entities/EntitiesConfigurations.java +++ b/envers/src/main/java/org/hibernate/envers/entities/EntitiesConfigurations.java @@ -32,12 +32,15 @@ import java.util.Map; */ public class EntitiesConfigurations { private Map entitiesConfigurations; + private Map notAuditedEntitiesConfigurations; // Map versions entity name -> entity name private Map entityNamesForVersionsEntityNames = new HashMap(); - public EntitiesConfigurations(Map entitiesConfigurations) { + public EntitiesConfigurations(Map entitiesConfigurations, + Map notAuditedEntitiesConfigurations) { this.entitiesConfigurations = entitiesConfigurations; + this.notAuditedEntitiesConfigurations = notAuditedEntitiesConfigurations; generateBidirectionRelationInfo(); generateVersionsEntityToEntityNames(); @@ -61,14 +64,17 @@ public class EntitiesConfigurations { // If this is an "owned" relation, checking the related entity, if it has a relation that has // a mapped-by attribute to the currently checked. If so, this is a bidirectional relation. if (relDesc.getRelationType() == RelationType.TO_ONE || - relDesc.getRelationType() == RelationType.TO_MANY_MIDDLE) { - for (RelationDescription other : entitiesConfigurations.get(relDesc.getToEntityName()).getRelationsIterator()) { - if (relDesc.getFromPropertyName().equals(other.getMappedByPropertyName()) && - (entityName.equals(other.getToEntityName()))) { - relDesc.setBidirectional(true); - other.setBidirectional(true); - } - } + relDesc.getRelationType() == RelationType.TO_MANY_MIDDLE) { + EntityConfiguration entityConfiguration = entitiesConfigurations.get(relDesc.getToEntityName()); + if (entityConfiguration != null) { + for (RelationDescription other : entityConfiguration.getRelationsIterator()) { + if (relDesc.getFromPropertyName().equals(other.getMappedByPropertyName()) && + (entityName.equals(other.getToEntityName()))) { + relDesc.setBidirectional(true); + other.setBidirectional(true); + } + } + } } } } @@ -78,6 +84,10 @@ public class EntitiesConfigurations { return entitiesConfigurations.get(entityName); } + public EntityConfiguration getNotVersionEntityConfiguration(String entityName) { + return notAuditedEntitiesConfigurations.get(entityName); + } + public String getEntityNameForVersionsEntityName(String versionsEntityName) { return entityNamesForVersionsEntityNames.get(versionsEntityName); } @@ -98,4 +108,5 @@ public class EntitiesConfigurations { return null; } } + } diff --git a/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java b/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java index 149b81ae89..953cab2c66 100644 --- a/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java +++ b/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java @@ -82,7 +82,7 @@ public class ToOneIdMapper implements PropertyMapper { Class entityClass = ReflectionTools.loadClass(referencedEntityName); value = versionsReader.getSessionImplementor().getFactory().getEntityPersister(referencedEntityName). - createProxy(null, new ToOneDelegateSessionImplementor(versionsReader, entityClass, entityId, revision)); + createProxy(null, new ToOneDelegateSessionImplementor(versionsReader, entityClass, entityId, revision, verCfg)); } } diff --git a/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/lazy/ToOneDelegateSessionImplementor.java b/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/lazy/ToOneDelegateSessionImplementor.java index 5a88ab41e9..6446c64f73 100644 --- a/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/lazy/ToOneDelegateSessionImplementor.java +++ b/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/lazy/ToOneDelegateSessionImplementor.java @@ -23,29 +23,47 @@ */ package org.hibernate.envers.entities.mapper.relation.lazy; +import java.io.Serializable; + +import org.hibernate.envers.configuration.AuditConfiguration; +import org.hibernate.envers.entities.EntitiesConfigurations; +import org.hibernate.envers.entities.EntityConfiguration; import org.hibernate.envers.reader.AuditReaderImplementor; import org.hibernate.HibernateException; +import org.hibernate.Session; /** * @author Adam Warski (adam at warski dot org) + * @author Tomasz Bech */ public class ToOneDelegateSessionImplementor extends AbstractDelegateSessionImplementor { + private static final long serialVersionUID = 4770438372940785488L; + private final AuditReaderImplementor versionsReader; private final Class entityClass; private final Object entityId; private final Number revision; + private EntityConfiguration notVersionedEntityConfiguration; - public ToOneDelegateSessionImplementor(AuditReaderImplementor versionsReader, - Class entityClass, Object entityId, Number revision) { + public ToOneDelegateSessionImplementor(AuditReaderImplementor versionsReader, + Class entityClass, Object entityId, Number revision, + AuditConfiguration verCfg) { super(versionsReader.getSessionImplementor()); this.versionsReader = versionsReader; this.entityClass = entityClass; this.entityId = entityId; this.revision = revision; + EntitiesConfigurations entCfg = verCfg.getEntCfg(); + notVersionedEntityConfiguration = entCfg.getNotVersionEntityConfiguration(entityClass.getName()); } public Object doImmediateLoad(String entityName) throws HibernateException { - return versionsReader.find(entityClass, entityId, revision); + if (notVersionedEntityConfiguration == null) { + return versionsReader.find(entityClass, entityId, revision); + } else { + Session session = versionsReader.getSession(); + return session.get(entityClass, (Serializable) entityId); + } } } diff --git a/envers/src/test/java/org/hibernate/envers/test/entities/manytoone/unidirectional/TargetNotAuditedEntity.java b/envers/src/test/java/org/hibernate/envers/test/entities/manytoone/unidirectional/TargetNotAuditedEntity.java new file mode 100644 index 0000000000..994cf3fbe9 --- /dev/null +++ b/envers/src/test/java/org/hibernate/envers/test/entities/manytoone/unidirectional/TargetNotAuditedEntity.java @@ -0,0 +1,115 @@ +/* + * 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.entities.manytoone.unidirectional; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.RelationTargetAuditMode; +import org.hibernate.envers.test.entities.UnversionedStrTestEntity; + +/** + * Audited entity with a reference to not audited entity. + * @author Toamsz Bech + */ +@Entity +public class TargetNotAuditedEntity { + @Id + private Integer id; + + @Audited + private String data; + + @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) + @ManyToOne(fetch = FetchType.EAGER) + private UnversionedStrTestEntity reference; + + public TargetNotAuditedEntity() { } + + public TargetNotAuditedEntity(Integer id, String data, UnversionedStrTestEntity reference) { + this.id = id; + this.data = data; + this.reference = reference; + } + + public TargetNotAuditedEntity(String data, UnversionedStrTestEntity reference) { + this.data = data; + this.reference = reference; + } + + public TargetNotAuditedEntity(Integer id, String data) { + this.id = id; + this.data = data; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public UnversionedStrTestEntity getReference() { + return reference; + } + + public void setReference(UnversionedStrTestEntity reference) { + this.reference = reference; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TargetNotAuditedEntity)) return false; + + TargetNotAuditedEntity that = (TargetNotAuditedEntity) o; + + if (data != null ? !data.equals(that.data) : that.data != null) return false; + if (id != null ? !id.equals(that.id) : that.id != null) return false; + + return true; + } + + public int hashCode() { + int result; + result = (id != null ? id.hashCode() : 0); + result = 31 * result + (data != null ? data.hashCode() : 0); + return result; + } + + public String toString() { + return "TargetNotAuditedEntity(id = " + id + ", data = " + data + ")"; + } +} diff --git a/envers/src/test/java/org/hibernate/envers/test/integration/manytoone/unidirectional/RelationNotAuditedTarget.java b/envers/src/test/java/org/hibernate/envers/test/integration/manytoone/unidirectional/RelationNotAuditedTarget.java new file mode 100644 index 0000000000..63a60df5c6 --- /dev/null +++ b/envers/src/test/java/org/hibernate/envers/test/integration/manytoone/unidirectional/RelationNotAuditedTarget.java @@ -0,0 +1,161 @@ +/* + * 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.manytoone.unidirectional; + +import java.util.Arrays; +import java.util.List; + +import javax.persistence.EntityManager; + +import org.hibernate.ejb.Ejb3Configuration; +import org.hibernate.envers.test.AbstractEntityTest; +import org.hibernate.envers.test.entities.UnversionedStrTestEntity; +import org.hibernate.envers.test.entities.manytoone.unidirectional.TargetNotAuditedEntity; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * @author Tomasz Bech + */ +public class RelationNotAuditedTarget extends AbstractEntityTest { + private Integer tnae1_id; + private Integer tnae2_id; + + private Integer uste1_id; + private Integer uste2_id; + + public void configure(Ejb3Configuration cfg) { + cfg.addAnnotatedClass(TargetNotAuditedEntity.class); + cfg.addAnnotatedClass(UnversionedStrTestEntity.class); + } + + @BeforeClass(dependsOnMethods = "init") + public void initData() { + EntityManager em = getEntityManager(); + + UnversionedStrTestEntity uste1 = new UnversionedStrTestEntity("str1"); + UnversionedStrTestEntity uste2 = new UnversionedStrTestEntity("str2"); + + // No revision + em.getTransaction().begin(); + + em.persist(uste1); + em.persist(uste2); + + em.getTransaction().commit(); + + // Revision 1 + em.getTransaction().begin(); + + uste1 = em.find(UnversionedStrTestEntity.class, uste1.getId()); + uste2 = em.find(UnversionedStrTestEntity.class, uste2.getId()); + + TargetNotAuditedEntity tnae1 = new TargetNotAuditedEntity(1, "tnae1", uste1); + TargetNotAuditedEntity tnae2 = new TargetNotAuditedEntity(2, "tnae2", uste2); + em.persist(tnae1); + em.persist(tnae2); + + em.getTransaction().commit(); + + // Revision 2 + em.getTransaction().begin(); + + tnae1 = em.find(TargetNotAuditedEntity.class, tnae1.getId()); + tnae2 = em.find(TargetNotAuditedEntity.class, tnae2.getId()); + + tnae1.setReference(uste2); + tnae2.setReference(uste1); + + em.getTransaction().commit(); + + // Revision 3 + em.getTransaction().begin(); + + tnae1 = em.find(TargetNotAuditedEntity.class, tnae1.getId()); + tnae2 = em.find(TargetNotAuditedEntity.class, tnae2.getId()); + + //field not changed!!! + tnae1.setReference(uste2); + tnae2.setReference(uste2); + + em.getTransaction().commit(); + + // Revision 4 + em.getTransaction().begin(); + + tnae1 = em.find(TargetNotAuditedEntity.class, tnae1.getId()); + tnae2 = em.find(TargetNotAuditedEntity.class, tnae2.getId()); + + tnae1.setReference(uste1); + tnae2.setReference(uste1); + + em.getTransaction().commit(); + + // + tnae1_id = tnae1.getId(); + tnae2_id = tnae2.getId(); + uste1_id = uste1.getId(); + uste2_id = uste2.getId(); + } + + @Test + public void testRevisionsCounts() { + List revisions = getAuditReader().getRevisions(TargetNotAuditedEntity.class, tnae1_id); + assert Arrays.asList(1, 2, 4).equals(revisions); + revisions = getAuditReader().getRevisions(TargetNotAuditedEntity.class, tnae2_id); + assert Arrays.asList(1, 2, 3, 4).equals(revisions); + } + + @Test + public void testHistoryOfTnae1_id() { + UnversionedStrTestEntity uste1 = getEntityManager().find(UnversionedStrTestEntity.class, uste1_id); + UnversionedStrTestEntity uste2 = getEntityManager().find(UnversionedStrTestEntity.class, uste2_id); + + TargetNotAuditedEntity rev1 = getAuditReader().find(TargetNotAuditedEntity.class, tnae1_id, 1); + TargetNotAuditedEntity rev2 = getAuditReader().find(TargetNotAuditedEntity.class, tnae1_id, 2); + TargetNotAuditedEntity rev3 = getAuditReader().find(TargetNotAuditedEntity.class, tnae1_id, 3); + TargetNotAuditedEntity rev4 = getAuditReader().find(TargetNotAuditedEntity.class, tnae1_id, 4); + + assert rev1.getReference().equals(uste1); + assert rev2.getReference().equals(uste2); + assert rev3.getReference().equals(uste2); + assert rev4.getReference().equals(uste1); + } + + @Test + public void testHistoryOfTnae2_id() { + UnversionedStrTestEntity uste1 = getEntityManager().find(UnversionedStrTestEntity.class, uste1_id); + UnversionedStrTestEntity uste2 = getEntityManager().find(UnversionedStrTestEntity.class, uste2_id); + + TargetNotAuditedEntity rev1 = getAuditReader().find(TargetNotAuditedEntity.class, tnae2_id, 1); + TargetNotAuditedEntity rev2 = getAuditReader().find(TargetNotAuditedEntity.class, tnae2_id, 2); + TargetNotAuditedEntity rev3 = getAuditReader().find(TargetNotAuditedEntity.class, tnae2_id, 3); + TargetNotAuditedEntity rev4 = getAuditReader().find(TargetNotAuditedEntity.class, tnae2_id, 4); + + assert rev1.getReference().equals(uste2); + assert rev2.getReference().equals(uste1); + assert rev3.getReference().equals(uste2); + assert rev4.getReference().equals(uste1); + } +}