diff --git a/documentation/src/main/docbook/devguide/en-US/Envers.xml b/documentation/src/main/docbook/devguide/en-US/Envers.xml index 2f04dc4469..feb03716b7 100644 --- a/documentation/src/main/docbook/devguide/en-US/Envers.xml +++ b/documentation/src/main/docbook/devguide/en-US/Envers.xml @@ -1304,7 +1304,8 @@ query.add(AuditEntity.relatedId("address").eq(relatedEntityId));]]> - collections of components + Bag style collection which identifier column has been defined using + @CollectionId annotation (JIRA ticket HHH-3950). diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java index c3cb4f3c6b..8695b69503 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java @@ -140,13 +140,13 @@ void addRevisionInfoRelation(Element any_mapping) { any_mapping.add(cloneAndSetupRevisionInfoRelationMapping()); } - void addRevisionType(Element any_mapping) { + void addRevisionType(Element any_mapping, Element any_mapping_end) { Element revTypeProperty = MetadataTools.addProperty(any_mapping, verEntCfg.getRevisionTypePropName(), verEntCfg.getRevisionTypePropType(), true, false); revTypeProperty.addAttribute("type", "org.hibernate.envers.entities.RevisionTypeType"); // Adding the end revision, if appropriate - addEndRevision(any_mapping); + addEndRevision(any_mapping_end); } private void addEndRevision(Element any_mapping ) { @@ -377,7 +377,7 @@ private Triple generateMappingData( class_mapping.add((Element) idMapper.getXmlMapping().clone()); // Adding the "revision type" property - addRevisionType(class_mapping); + addRevisionType(class_mapping, class_mapping); return Triple.make(class_mapping, propertyMapper, null); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java index 907212bb82..e10964cff2 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java @@ -38,13 +38,18 @@ import org.jboss.logging.Logger; import org.hibernate.MappingException; +import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.envers.ModificationStore; import org.hibernate.envers.RelationTargetAuditMode; +import org.hibernate.envers.configuration.metadata.reader.AuditedPropertiesReader; +import org.hibernate.envers.configuration.metadata.reader.ComponentAuditedPropertiesReader; +import org.hibernate.envers.configuration.metadata.reader.ComponentAuditingData; import org.hibernate.envers.configuration.metadata.reader.PropertyAuditingData; import org.hibernate.envers.entities.EntityConfiguration; import org.hibernate.envers.entities.IdMappingData; import org.hibernate.envers.entities.PropertyData; import org.hibernate.envers.entities.mapper.CompositeMapperBuilder; +import org.hibernate.envers.entities.mapper.MultiPropertyMapper; import org.hibernate.envers.entities.mapper.PropertyMapper; import org.hibernate.envers.entities.mapper.SinglePropertyMapper; import org.hibernate.envers.entities.mapper.id.IdMapper; @@ -58,6 +63,7 @@ import org.hibernate.envers.entities.mapper.relation.SortedSetCollectionMapper; import org.hibernate.envers.entities.mapper.relation.ToOneIdMapper; import org.hibernate.envers.entities.mapper.relation.component.MiddleDummyComponentMapper; +import org.hibernate.envers.entities.mapper.relation.component.MiddleEmbeddableComponentMapper; import org.hibernate.envers.entities.mapper.relation.component.MiddleMapKeyIdComponentMapper; import org.hibernate.envers.entities.mapper.relation.component.MiddleMapKeyPropertyComponentMapper; import org.hibernate.envers.entities.mapper.relation.component.MiddleRelatedComponentMapper; @@ -75,6 +81,7 @@ import org.hibernate.envers.tools.StringTools; import org.hibernate.envers.tools.Tools; import org.hibernate.mapping.Collection; +import org.hibernate.mapping.Component; import org.hibernate.mapping.IndexedCollection; import org.hibernate.mapping.ManyToOne; import org.hibernate.mapping.OneToMany; @@ -83,6 +90,7 @@ import org.hibernate.mapping.Table; import org.hibernate.mapping.Value; import org.hibernate.type.BagType; +import org.hibernate.type.ComponentType; import org.hibernate.type.ListType; import org.hibernate.type.ManyToOneType; import org.hibernate.type.MapType; @@ -203,7 +211,7 @@ private void addOneToManyAttached(boolean fakeOneToManyBidirectional) { // Generating the query generator - it should read directly from the related entity. RelationQueryGenerator queryGenerator = new OneAuditEntityQueryGenerator(mainGenerator.getGlobalCfg(), mainGenerator.getVerEntCfg(), mainGenerator.getAuditStrategy(), - referencingIdData, referencedEntityName, referencedIdData); + referencingIdData, referencedEntityName, referencedIdData, isEmbeddableElementType()); // Creating common mapper data. CommonCollectionMapperData commonCollectionMapperData = new CommonCollectionMapperData( @@ -351,7 +359,7 @@ private void addWithMiddleTable() { // a query generator to read the raw data collection from the middle table. QueryGeneratorBuilder queryGeneratorBuilder = new QueryGeneratorBuilder(mainGenerator.getGlobalCfg(), mainGenerator.getVerEntCfg(), mainGenerator.getAuditStrategy(), referencingIdData, - auditMiddleEntityName); + auditMiddleEntityName, isEmbeddableElementType()); // Adding the XML mapping for the referencing entity, if the relation isn't inverse. if (middleEntityXml != null) { @@ -463,6 +471,41 @@ private MiddleComponentData addValueToMiddleTable(Value value, Element xmlMappin return new MiddleComponentData(new MiddleRelatedComponentMapper(referencedIdData), queryGeneratorBuilder.getCurrentIndex()); + } else if ( type instanceof ComponentType ) { + // Collection of embeddable elements. + final Component component = (Component) value; + final MiddleEmbeddableComponentMapper componentMapper = new MiddleEmbeddableComponentMapper( new MultiPropertyMapper(), component.getComponentClassName() ); + + final Element parentXmlMapping = xmlMapping.getParent(); + final ComponentAuditingData auditData = new ComponentAuditingData(); + final ReflectionManager reflectionManager = mainGenerator.getCfg().getReflectionManager(); + + new ComponentAuditedPropertiesReader( + ModificationStore.FULL, + new AuditedPropertiesReader.ComponentPropertiesSource( reflectionManager, component ), + auditData, mainGenerator.getGlobalCfg(), reflectionManager, "" + ).read(); + + // Emulating first pass. + for ( String auditedPropertyName : auditData.getPropertyNames() ) { + PropertyAuditingData nestedAuditingData = auditData.getPropertyAuditingData( auditedPropertyName ); + mainGenerator.addValue( + parentXmlMapping, component.getProperty( auditedPropertyName ).getValue(), componentMapper, + prefix, xmlMappingData, nestedAuditingData, true, true, true + ); + } + + // Emulating second pass so that the relations can be mapped too. + for ( String auditedPropertyName : auditData.getPropertyNames() ) { + PropertyAuditingData nestedAuditingData = auditData.getPropertyAuditingData( auditedPropertyName ); + mainGenerator.addValue( + parentXmlMapping, component.getProperty( auditedPropertyName ).getValue(), + componentMapper, referencingEntityName, xmlMappingData, nestedAuditingData, + true, false, true + ); + } + + return new MiddleComponentData( componentMapper, 0 ); } else { // Last but one parameter: collection components are always insertable boolean mapped = mainGenerator.getBasicMetadataGenerator().addBasic(xmlMapping, @@ -483,33 +526,36 @@ private MiddleComponentData addValueToMiddleTable(Value value, Element xmlMappin private void addMapper(CommonCollectionMapperData commonCollectionMapperData, MiddleComponentData elementComponentData, MiddleComponentData indexComponentData) { Type type = propertyValue.getType(); + boolean embeddableElementType = isEmbeddableElementType(); if (type instanceof SortedSetType) { currentMapper.addComposite(propertyAuditingData.getPropertyData(), new SortedSetCollectionMapper(commonCollectionMapperData, - TreeSet.class, SortedSetProxy.class, elementComponentData, propertyValue.getComparator())); + TreeSet.class, SortedSetProxy.class, elementComponentData, propertyValue.getComparator(), + embeddableElementType)); } else if (type instanceof SetType) { currentMapper.addComposite(propertyAuditingData.getPropertyData(), new BasicCollectionMapper(commonCollectionMapperData, - HashSet.class, SetProxy.class, elementComponentData)); + HashSet.class, SetProxy.class, elementComponentData, embeddableElementType)); } else if (type instanceof SortedMapType) { // Indexed collection, so indexComponentData is not null. currentMapper.addComposite(propertyAuditingData.getPropertyData(), new SortedMapCollectionMapper(commonCollectionMapperData, - TreeMap.class, SortedMapProxy.class, elementComponentData, indexComponentData, propertyValue.getComparator())); + TreeMap.class, SortedMapProxy.class, elementComponentData, indexComponentData, propertyValue.getComparator(), + embeddableElementType)); } else if (type instanceof MapType) { // Indexed collection, so indexComponentData is not null. currentMapper.addComposite(propertyAuditingData.getPropertyData(), new MapCollectionMapper(commonCollectionMapperData, - HashMap.class, MapProxy.class, elementComponentData, indexComponentData)); + HashMap.class, MapProxy.class, elementComponentData, indexComponentData, embeddableElementType)); } else if (type instanceof BagType) { currentMapper.addComposite(propertyAuditingData.getPropertyData(), new BasicCollectionMapper(commonCollectionMapperData, - ArrayList.class, ListProxy.class, elementComponentData)); + ArrayList.class, ListProxy.class, elementComponentData, embeddableElementType)); } else if (type instanceof ListType) { // Indexed collection, so indexComponentData is not null. currentMapper.addComposite(propertyAuditingData.getPropertyData(), new ListCollectionMapper(commonCollectionMapperData, - elementComponentData, indexComponentData)); + elementComponentData, indexComponentData, embeddableElementType)); } else { mainGenerator.throwUnsupportedTypeException(type, referencingEntityName, propertyName); } @@ -546,12 +592,19 @@ private Element createMiddleEntityXml(String auditMiddleTableName, String auditM mainGenerator.addRevisionInfoRelation(middleEntityXmlId); // Adding the revision type property to the entity xml. - mainGenerator.addRevisionType(middleEntityXml); + mainGenerator.addRevisionType(isEmbeddableElementType() ? middleEntityXmlId : middleEntityXml, middleEntityXml); // All other properties should also be part of the primary key of the middle entity. return middleEntityXmlId; } + /** + * Checks if the collection element is of {@link ComponentType} type. + */ + private boolean isEmbeddableElementType() { + return propertyValue.getElement().getType() instanceof ComponentType; + } + private String getMappedBy(Collection collectionValue) { PersistentClass referencedClass = null; if (collectionValue.getElement() instanceof OneToMany) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/QueryGeneratorBuilder.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/QueryGeneratorBuilder.java index 6735e3aa71..bc210a0af9 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/QueryGeneratorBuilder.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/QueryGeneratorBuilder.java @@ -49,15 +49,17 @@ public final class QueryGeneratorBuilder { private final MiddleIdData referencingIdData; private final String auditMiddleEntityName; private final List idDatas; + private final boolean revisionTypeInId; QueryGeneratorBuilder(GlobalConfiguration globalCfg, AuditEntitiesConfiguration verEntCfg, - AuditStrategy auditStrategy, - MiddleIdData referencingIdData, String auditMiddleEntityName) { + AuditStrategy auditStrategy, MiddleIdData referencingIdData, String auditMiddleEntityName, + boolean revisionTypeInId) { this.globalCfg = globalCfg; this.verEntCfg = verEntCfg; this.auditStrategy = auditStrategy; this.referencingIdData = referencingIdData; this.auditMiddleEntityName = auditMiddleEntityName; + this.revisionTypeInId = revisionTypeInId; idDatas = new ArrayList(); } @@ -69,14 +71,14 @@ void addRelation(MiddleIdData idData) { RelationQueryGenerator build(MiddleComponentData... componentDatas) { if (idDatas.size() == 0) { return new OneEntityQueryGenerator(verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData, - componentDatas); + revisionTypeInId, componentDatas); } else if (idDatas.size() == 1) { if (idDatas.get(0).isAudited()) { return new TwoEntityQueryGenerator(globalCfg, verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData, - idDatas.get(0), componentDatas); + idDatas.get(0), revisionTypeInId, componentDatas); } else { return new TwoEntityOneAuditedQueryGenerator(verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData, - idDatas.get(0), componentDatas); + idDatas.get(0), revisionTypeInId, componentDatas); } } else if (idDatas.size() == 2) { // All entities must be audited. @@ -85,7 +87,7 @@ RelationQueryGenerator build(MiddleComponentData... componentDatas) { } return new ThreeEntityQueryGenerator(globalCfg, verEntCfg, auditStrategy, auditMiddleEntityName, referencingIdData, - idDatas.get(0), idDatas.get(1), componentDatas); + idDatas.get(0), idDatas.get(1), revisionTypeInId, componentDatas); } else { throw new IllegalStateException("Illegal number of related entities."); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java index b9067cec8e..3833cf59e0 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java @@ -319,7 +319,7 @@ private void addFromPropertiesGroup(String embeddedName, XProperty property, Str // Marking component properties as placed directly in class (not inside another component). componentData.setBeanName(null); - PersistentPropertiesSource componentPropertiesSource = new ComponentPropertiesSource((Component) propertyValue); + PersistentPropertiesSource componentPropertiesSource = new ComponentPropertiesSource( reflectionManager, propertyValue ); AuditedPropertiesReader audPropReader = new AuditedPropertiesReader( ModificationStore.FULL, componentPropertiesSource, componentData, globalCfg, reflectionManager, propertyNamePrefix + MappingTools.createComponentPrefix(embeddedName) @@ -338,7 +338,8 @@ private void addFromComponentProperty(XProperty property, allClassAudited); PersistentPropertiesSource componentPropertiesSource = new ComponentPropertiesSource( - (Component) propertyValue); + reflectionManager, propertyValue + ); ComponentAuditedPropertiesReader audPropReader = new ComponentAuditedPropertiesReader( ModificationStore.FULL, componentPropertiesSource, @@ -540,11 +541,11 @@ private boolean processPropertyAuditingOverrides(XProperty property, PropertyAud public Class annotationType() { return this.getClass(); } }; - private class ComponentPropertiesSource implements PersistentPropertiesSource { + public static class ComponentPropertiesSource implements PersistentPropertiesSource { private final XClass xclass; private final Component component; - private ComponentPropertiesSource(Component component) { + public ComponentPropertiesSource(ReflectionManager reflectionManager, Component component) { try { this.xclass = reflectionManager.classForName(component.getComponentClassName(), this.getClass()); } catch (ClassNotFoundException e) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/ComponentAuditingData.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/ComponentAuditingData.java index 104a7664a4..b6108477b5 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/ComponentAuditingData.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/ComponentAuditingData.java @@ -24,6 +24,7 @@ */ package org.hibernate.envers.configuration.metadata.reader; import java.util.Map; +import java.util.Set; import static org.hibernate.envers.tools.Tools.newHashMap; @@ -53,5 +54,9 @@ public PropertyAuditingData getPropertyAuditingData(String propertyName) { public boolean contains(String propertyName) { return properties.containsKey(propertyName); - } + } + + public Set getPropertyNames() { + return properties.keySet(); + } } \ No newline at end of file diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/EntityInstantiator.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/EntityInstantiator.java index 5152539e08..b8758b9b74 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/EntityInstantiator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/EntityInstantiator.java @@ -25,11 +25,9 @@ import org.hibernate.envers.configuration.AuditConfiguration; import org.hibernate.envers.entities.mapper.id.IdMapper; -import org.hibernate.envers.entities.mapper.id.MultipleIdMapper; import org.hibernate.envers.entities.mapper.relation.lazy.ToOneDelegateSessionImplementor; import org.hibernate.envers.exception.AuditException; import org.hibernate.envers.reader.AuditReaderImplementor; -import org.hibernate.envers.tools.reflection.ReflectionTools; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; @@ -149,4 +147,12 @@ public void addInstancesFromVersionsEntities(String entityName, Collection addTo addTo.add(createInstanceFromVersionsEntity(entityName, versionsEntity, revision)); } } + + public AuditConfiguration getAuditConfiguration() { + return verCfg; + } + + public AuditReaderImplementor getAuditReaderImplementor() { + return versionsReader; + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/ComponentPropertyMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/ComponentPropertyMapper.java index 871cd3da3e..045adf7813 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/ComponentPropertyMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/ComponentPropertyMapper.java @@ -130,11 +130,13 @@ public void mapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, } } - public List mapCollectionChanges(String referencingPropertyName, - PersistentCollection newColl, - Serializable oldColl, - Serializable id) { - return delegate.mapCollectionChanges(referencingPropertyName, newColl, oldColl, id); + public List mapCollectionChanges(SessionImplementor session, String referencingPropertyName, + PersistentCollection newColl, + Serializable oldColl, Serializable id) { + return delegate.mapCollectionChanges(session, referencingPropertyName, newColl, oldColl, id); } + public Map getProperties() { + return delegate.getProperties(); + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/CompositeMapperBuilder.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/CompositeMapperBuilder.java index 6f1e9ba055..420c47fe90 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/CompositeMapperBuilder.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/CompositeMapperBuilder.java @@ -22,6 +22,9 @@ * Boston, MA 02110-1301 USA */ package org.hibernate.envers.entities.mapper; + +import java.util.Map; + import org.hibernate.envers.entities.PropertyData; /** @@ -30,4 +33,5 @@ public interface CompositeMapperBuilder extends SimpleMapperBuilder { public CompositeMapperBuilder addComponent(PropertyData propertyData, String componentClassName); public void addComposite(PropertyData propertyData, PropertyMapper propertyMapper); + public Map getProperties(); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/MultiPropertyMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/MultiPropertyMapper.java index 4dbfe3a272..018a4c90ed 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/MultiPropertyMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/MultiPropertyMapper.java @@ -171,14 +171,14 @@ public void mapModifiedFlagsToMapForCollectionChange(String collectionPropertyNa } } - public List mapCollectionChanges(String referencingPropertyName, - PersistentCollection newColl, - Serializable oldColl, - Serializable id) { + public List mapCollectionChanges(SessionImplementor session, + String referencingPropertyName, + PersistentCollection newColl, + Serializable oldColl, Serializable id) { Pair pair = getMapperAndDelegatePropName(referencingPropertyName); PropertyMapper mapper = pair.getFirst(); if (mapper != null) { - return mapper.mapCollectionChanges(pair.getSecond(), newColl, oldColl, id); + return mapper.mapCollectionChanges(session, pair.getSecond(), newColl, oldColl, id); } else { return null; } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/PropertyMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/PropertyMapper.java index d584c614c8..d2b1bfcfb5 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/PropertyMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/PropertyMapper.java @@ -59,14 +59,15 @@ void mapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object AuditReaderImplementor versionsReader, Number revision); /** - * Maps collection changes + * Maps collection changes. + * @param session The current session. * @param referencingPropertyName Name of the field, which holds the collection in the entity. * @param newColl New collection, after updates. * @param oldColl Old collection, before updates. * @param id Id of the object owning the collection. * @return List of changes that need to be performed on the persistent store. */ - List mapCollectionChanges(String referencingPropertyName, + List mapCollectionChanges(SessionImplementor session, String referencingPropertyName, PersistentCollection newColl, Serializable oldColl, Serializable id); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/SinglePropertyMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/SinglePropertyMapper.java index e1c8c898c6..fd20bec1fe 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/SinglePropertyMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/SinglePropertyMapper.java @@ -115,10 +115,10 @@ private boolean isPrimitive(Setter setter, PropertyData propertyData, Class c } } - public List mapCollectionChanges(String referencingPropertyName, + public List mapCollectionChanges(SessionImplementor sessionImplementor, + String referencingPropertyName, PersistentCollection newColl, - Serializable oldColl, - Serializable id) { + Serializable oldColl, Serializable id) { return null; } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/SubclassPropertyMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/SubclassPropertyMapper.java index 581bf76f7b..a9a703f936 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/SubclassPropertyMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/SubclassPropertyMapper.java @@ -23,6 +23,7 @@ */ package org.hibernate.envers.entities.mapper; import java.io.Serializable; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -78,15 +79,14 @@ public void mapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, main.mapToEntityFromMap(verCfg, obj, data, primaryKey, versionsReader, revision); } - public List mapCollectionChanges(String referencingPropertyName, - PersistentCollection newColl, - Serializable oldColl, - Serializable id) { + public List mapCollectionChanges(SessionImplementor session, String referencingPropertyName, + PersistentCollection newColl, + Serializable oldColl, Serializable id) { List parentCollectionChanges = parentMapper.mapCollectionChanges( - referencingPropertyName, newColl, oldColl, id); + session, referencingPropertyName, newColl, oldColl, id); List mainCollectionChanges = main.mapCollectionChanges( - referencingPropertyName, newColl, oldColl, id); + session, referencingPropertyName, newColl, oldColl, id); if (parentCollectionChanges == null) { return mainCollectionChanges; @@ -109,4 +109,11 @@ public void addComposite(PropertyData propertyData, PropertyMapper propertyMappe public void add(PropertyData propertyData) { main.add(propertyData); } + + public Map getProperties() { + final Map joinedProperties = new HashMap(); + joinedProperties.putAll(parentMapper.getProperties()); + joinedProperties.putAll(main.getProperties()); + return joinedProperties; + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/AbstractCollectionMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/AbstractCollectionMapper.java index 8838cc76d6..756f4b857d 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/AbstractCollectionMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/AbstractCollectionMapper.java @@ -22,6 +22,7 @@ * Boston, MA 02110-1301 USA */ package org.hibernate.envers.entities.mapper.relation; + import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -54,13 +55,16 @@ public abstract class AbstractCollectionMapper implements PropertyMapper { protected final CommonCollectionMapperData commonCollectionMapperData; protected final Class collectionClass; + protected final boolean revisionTypeInId; private final Constructor proxyConstructor; protected AbstractCollectionMapper(CommonCollectionMapperData commonCollectionMapperData, - Class collectionClass, Class proxyClass) { + Class collectionClass, Class proxyClass, + boolean revisionTypeInId) { this.commonCollectionMapperData = commonCollectionMapperData; this.collectionClass = collectionClass; + this.revisionTypeInId = revisionTypeInId; try { proxyConstructor = proxyClass.getConstructor(Initializor.class); @@ -74,13 +78,14 @@ protected AbstractCollectionMapper(CommonCollectionMapperData commonCollectionMa /** * Maps the changed collection element to the given map. + * @param idData Map to which composite-id data should be added. * @param data Where to map the data. * @param changed The changed collection element to map. */ - protected abstract void mapToMapFromObject(Map data, Object changed); + protected abstract void mapToMapFromObject(SessionImplementor session, Map idData, Map data, Object changed); - private void addCollectionChanges(List collectionChanges, Set changed, - RevisionType revisionType, Serializable id) { + private void addCollectionChanges(SessionImplementor session, List collectionChanges, + Set changed, RevisionType revisionType, Serializable id) { for (Object changedObj : changed) { Map entityData = new HashMap(); Map originalId = new HashMap(); @@ -92,18 +97,18 @@ private void addCollectionChanges(List collectio commonCollectionMapperData.getReferencingIdData().getPrefixedMapper().mapToMapFromId(originalId, id); // Mapping collection element and index (if present). - mapToMapFromObject(originalId, changedObj); + mapToMapFromObject(session, originalId, entityData, changedObj); - entityData.put(commonCollectionMapperData.getVerEntCfg().getRevisionTypePropName(), revisionType); + (revisionTypeInId ? originalId : entityData).put(commonCollectionMapperData.getVerEntCfg().getRevisionTypePropName(), revisionType); } } @SuppressWarnings({"unchecked"}) - public List mapCollectionChanges(String referencingPropertyName, + public List mapCollectionChanges(SessionImplementor session, + String referencingPropertyName, PersistentCollection newColl, Serializable oldColl, Serializable id) { - if (!commonCollectionMapperData.getCollectionReferencingPropertyData().getName() - .equals(referencingPropertyName)) { + if (!commonCollectionMapperData.getCollectionReferencingPropertyData().getName().equals(referencingPropertyName)) { return null; } @@ -119,14 +124,14 @@ public List mapCollectionChanges(String referenc // removeAll in AbstractSet has an implementation that is hashcode-change sensitive (as opposed to addAll). if (oldColl != null) { added.removeAll(new HashSet(oldCollection)); } - addCollectionChanges(collectionChanges, added, RevisionType.ADD, id); + addCollectionChanges(session, collectionChanges, added, RevisionType.ADD, id); Set deleted = new HashSet(); if (oldColl != null) { deleted.addAll(oldCollection); } // The same as above - re-hashing new collection. if (newColl != null) { deleted.removeAll(new HashSet(newCollection)); } - addCollectionChanges(collectionChanges, deleted, RevisionType.DEL, id); + addCollectionChanges(session, collectionChanges, deleted, RevisionType.DEL, id); return collectionChanges; } @@ -146,7 +151,7 @@ public void mapModifiedFlagsToMapFromEntity(SessionImplementor session, Map changes = mapCollectionChanges( + List changes = mapCollectionChanges(session, commonCollectionMapperData.getCollectionReferencingPropertyData().getName(), (PersistentCollection) newObj, (Serializable) oldObj, null); data.put(propertyData.getModifiedFlagPropertyName(), !changes.isEmpty()); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/AbstractToOneMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/AbstractToOneMapper.java index fd0f9f8508..b47a2cd3a7 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/AbstractToOneMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/AbstractToOneMapper.java @@ -42,9 +42,9 @@ public void mapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, } @Override - public List mapCollectionChanges(String referencingPropertyName, - PersistentCollection newColl, - Serializable oldColl, Serializable id) { + public List mapCollectionChanges(SessionImplementor session, String referencingPropertyName, + PersistentCollection newColl, Serializable oldColl, + Serializable id) { return null; } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/BasicCollectionMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/BasicCollectionMapper.java index a17c908dab..1c934f6f7a 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/BasicCollectionMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/BasicCollectionMapper.java @@ -28,6 +28,7 @@ import java.util.Map; import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.envers.configuration.AuditConfiguration; import org.hibernate.envers.entities.mapper.PropertyMapper; import org.hibernate.envers.entities.mapper.relation.lazy.initializor.BasicCollectionInitializor; @@ -42,8 +43,8 @@ public class BasicCollectionMapper extends AbstractCollect public BasicCollectionMapper(CommonCollectionMapperData commonCollectionMapperData, Class collectionClass, Class proxyClass, - MiddleComponentData elementComponentData) { - super(commonCollectionMapperData, collectionClass, proxyClass); + MiddleComponentData elementComponentData, boolean revisionTypeInId) { + super(commonCollectionMapperData, collectionClass, proxyClass, revisionTypeInId); this.elementComponentData = elementComponentData; } @@ -67,7 +68,7 @@ protected Collection getOldCollectionContent(Serializable oldCollection) { } } - protected void mapToMapFromObject(Map data, Object changed) { - elementComponentData.getComponentMapper().mapToMapFromObject(data, changed); + protected void mapToMapFromObject(SessionImplementor session, Map idData, Map data, Object changed) { + elementComponentData.getComponentMapper().mapToMapFromObject(session, idData, data, changed); } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ListCollectionMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ListCollectionMapper.java index 1bb3022cdc..f883e68a79 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ListCollectionMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ListCollectionMapper.java @@ -28,6 +28,7 @@ import java.util.Map; import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.envers.configuration.AuditConfiguration; import org.hibernate.envers.entities.mapper.PropertyMapper; import org.hibernate.envers.entities.mapper.relation.lazy.initializor.Initializor; @@ -45,8 +46,9 @@ public final class ListCollectionMapper extends AbstractCollectionMapper i private final MiddleComponentData indexComponentData; public ListCollectionMapper(CommonCollectionMapperData commonCollectionMapperData, - MiddleComponentData elementComponentData, MiddleComponentData indexComponentData) { - super(commonCollectionMapperData, List.class, ListProxy.class); + MiddleComponentData elementComponentData, MiddleComponentData indexComponentData, + boolean revisionTypeInId) { + super(commonCollectionMapperData, List.class, ListProxy.class, revisionTypeInId); this.elementComponentData = elementComponentData; this.indexComponentData = indexComponentData; } @@ -76,9 +78,9 @@ protected Collection getOldCollectionContent(Serializable oldCollection) { } @SuppressWarnings({"unchecked"}) - protected void mapToMapFromObject(Map data, Object changed) { + protected void mapToMapFromObject(SessionImplementor session, Map idData, Map data, Object changed) { Pair indexValuePair = (Pair) changed; - elementComponentData.getComponentMapper().mapToMapFromObject(data, indexValuePair.getSecond()); - indexComponentData.getComponentMapper().mapToMapFromObject(data, indexValuePair.getFirst()); + elementComponentData.getComponentMapper().mapToMapFromObject(session, idData, data, indexValuePair.getSecond()); + indexComponentData.getComponentMapper().mapToMapFromObject(session, idData, data, indexValuePair.getFirst()); } } \ No newline at end of file diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/MapCollectionMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/MapCollectionMapper.java index 519b7db038..44f1ec8827 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/MapCollectionMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/MapCollectionMapper.java @@ -28,6 +28,7 @@ import java.util.Map; import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.envers.configuration.AuditConfiguration; import org.hibernate.envers.entities.mapper.PropertyMapper; import org.hibernate.envers.entities.mapper.relation.lazy.initializor.Initializor; @@ -43,8 +44,9 @@ public class MapCollectionMapper extends AbstractCollectionMapper public MapCollectionMapper(CommonCollectionMapperData commonCollectionMapperData, Class collectionClass, Class proxyClass, - MiddleComponentData elementComponentData, MiddleComponentData indexComponentData) { - super(commonCollectionMapperData, collectionClass, proxyClass); + MiddleComponentData elementComponentData, MiddleComponentData indexComponentData, + boolean revisionTypeInId) { + super(commonCollectionMapperData, collectionClass, proxyClass, revisionTypeInId); this.elementComponentData = elementComponentData; this.indexComponentData = indexComponentData; } @@ -71,8 +73,8 @@ protected Collection getOldCollectionContent(Serializable oldCollection) { } } - protected void mapToMapFromObject(Map data, Object changed) { - elementComponentData.getComponentMapper().mapToMapFromObject(data, ((Map.Entry) changed).getValue()); - indexComponentData.getComponentMapper().mapToMapFromObject(data, ((Map.Entry) changed).getKey()); + protected void mapToMapFromObject(SessionImplementor session, Map idData, Map data, Object changed) { + elementComponentData.getComponentMapper().mapToMapFromObject(session, idData, data, ((Map.Entry) changed).getValue()); + indexComponentData.getComponentMapper().mapToMapFromObject(session, idData, data, ((Map.Entry) changed).getKey()); } } \ No newline at end of file diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/SortedMapCollectionMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/SortedMapCollectionMapper.java index ab04a8d96d..a3a512ebd1 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/SortedMapCollectionMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/SortedMapCollectionMapper.java @@ -39,8 +39,9 @@ public final class SortedMapCollectionMapper extends MapCollectionMapper collectionClass, Class proxyClass, - MiddleComponentData elementComponentData, MiddleComponentData indexComponentData, Comparator comparator) { - super(commonCollectionMapperData, collectionClass, proxyClass, elementComponentData, indexComponentData); + MiddleComponentData elementComponentData, MiddleComponentData indexComponentData, Comparator comparator, + boolean revisionTypeInId) { + super(commonCollectionMapperData, collectionClass, proxyClass, elementComponentData, indexComponentData, revisionTypeInId); this.comparator = comparator; } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/SortedSetCollectionMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/SortedSetCollectionMapper.java index b5555fe8c7..7c4a3b451a 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/SortedSetCollectionMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/SortedSetCollectionMapper.java @@ -39,8 +39,9 @@ public final class SortedSetCollectionMapper extends BasicCollectionMapper collectionClass, Class proxyClass, - MiddleComponentData elementComponentData, Comparator comparator) { - super(commonCollectionMapperData, collectionClass, proxyClass, elementComponentData); + MiddleComponentData elementComponentData, Comparator comparator, + boolean revisionTypeInId) { + super(commonCollectionMapperData, collectionClass, proxyClass, elementComponentData, revisionTypeInId); this.comparator = comparator; } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java index 308ffe42ba..d322340638 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java @@ -34,6 +34,7 @@ import org.hibernate.envers.entities.mapper.relation.lazy.ToOneDelegateSessionImplementor; import org.hibernate.envers.reader.AuditReaderImplementor; import org.hibernate.envers.tools.Tools; +import org.hibernate.envers.tools.query.Parameters; import org.hibernate.persister.entity.EntityPersister; /** @@ -105,4 +106,8 @@ public void nullSafeMapToEntityFromMap(AuditConfiguration verCfg, Object obj, Ma setPropertyValue(obj, value); } + + public void addMiddleEqualToQuery(Parameters parameters, String idPrefix1, String prefix1, String idPrefix2, String prefix2) { + delegate.addIdsEqualToQuery( parameters, prefix1, delegate, prefix2 ); + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleComponentMapper.java index 3630509b01..73775eb4c8 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleComponentMapper.java @@ -22,8 +22,12 @@ * Boston, MA 02110-1301 USA */ package org.hibernate.envers.entities.mapper.relation.component; -import java.util.Map; +import java.util.Map; +import javax.persistence.ElementCollection; +import javax.persistence.Embeddable; + +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.envers.entities.EntityInstantiator; import org.hibernate.envers.tools.query.Parameters; @@ -45,18 +49,22 @@ Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map data, Object obj); + void mapToMapFromObject(SessionImplementor session, Map idData, Map data, Object obj); /** * Adds query statements, which contains restrictions, which express the property that part of the middle * entity with alias prefix1, is equal to part of the middle entity with alias prefix2 (the entity is the same). * The part is the component's representation in the middle entity. * @param parameters Parameters, to which to add the statements. + * @param idPrefix1 First alias of the entity + prefix + id to add to the properties. * @param prefix1 First alias of the entity + prefix to add to the properties. + * @param idPrefix2 Second alias of the entity + prefix + id to add to the properties. * @param prefix2 Second alias of the entity + prefix to add to the properties. */ - void addMiddleEqualToQuery(Parameters parameters, String prefix1, String prefix2); + void addMiddleEqualToQuery(Parameters parameters, String idPrefix1, String prefix1, String idPrefix2, String prefix2); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleDummyComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleDummyComponentMapper.java index 1c733a7b5c..8c681b937f 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleDummyComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleDummyComponentMapper.java @@ -24,6 +24,7 @@ package org.hibernate.envers.entities.mapper.relation.component; import java.util.Map; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.envers.entities.EntityInstantiator; import org.hibernate.envers.tools.query.Parameters; @@ -36,9 +37,9 @@ public Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map< return null; } - public void mapToMapFromObject(Map data, Object obj) { + public void mapToMapFromObject(SessionImplementor session, Map idData, Map data, Object obj) { } - public void addMiddleEqualToQuery(Parameters parameters, String prefix1, String prefix2) { + public void addMiddleEqualToQuery(Parameters parameters, String idPrefix1, String prefix1, String idPrefix2, String prefix2) { } } \ No newline at end of file diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleEmbeddableComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleEmbeddableComponentMapper.java new file mode 100644 index 0000000000..c287ff6c8d --- /dev/null +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleEmbeddableComponentMapper.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.entities.mapper.relation.component; + +import java.util.Map; + +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.envers.entities.EntityInstantiator; +import org.hibernate.envers.entities.PropertyData; +import org.hibernate.envers.entities.mapper.CompositeMapperBuilder; +import org.hibernate.envers.entities.mapper.MultiPropertyMapper; +import org.hibernate.envers.entities.mapper.PropertyMapper; +import org.hibernate.envers.entities.mapper.relation.ToOneIdMapper; +import org.hibernate.envers.exception.AuditException; +import org.hibernate.envers.tools.query.Parameters; +import org.hibernate.internal.util.ReflectHelper; + +/** + * @author Kristoffer Lundberg (kristoffer at cambio dot se) + */ +public class MiddleEmbeddableComponentMapper implements MiddleComponentMapper, CompositeMapperBuilder { + private final MultiPropertyMapper delegate; + private final Class componentClass; + + public MiddleEmbeddableComponentMapper(MultiPropertyMapper delegate, String componentClassName) { + this.delegate = delegate; + try { + componentClass = Thread.currentThread().getContextClassLoader().loadClass( componentClassName ); + } + catch ( Exception e ) { + throw new AuditException( e ); + } + } + + @Override + public Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map data, Object dataObject, Number revision) { + try { + final Object componentInstance = dataObject != null ? dataObject : ReflectHelper.getDefaultConstructor( componentClass ).newInstance(); + delegate.mapToEntityFromMap( + entityInstantiator.getAuditConfiguration(), componentInstance, data, null, + entityInstantiator.getAuditReaderImplementor(), revision + ); + return componentInstance; + } + catch ( Exception e ) { + throw new AuditException( e ); + } + } + + @Override + public void mapToMapFromObject(SessionImplementor session, Map idData, Map data, Object obj) { + delegate.mapToMapFromEntity( session, data, obj, obj ); + } + + @Override + public void addMiddleEqualToQuery(Parameters parameters, String idPrefix1, String prefix1, String idPrefix2, String prefix2) { + addMiddleEqualToQuery( delegate, parameters, idPrefix1, prefix1, idPrefix2, prefix2 ); + } + + protected void addMiddleEqualToQuery(CompositeMapperBuilder compositeMapper, Parameters parameters, String idPrefix1, String prefix1, String idPrefix2, String prefix2) { + for ( final Map.Entry entry : compositeMapper.getProperties().entrySet() ) { + final String propertyName = entry.getKey().getName(); + final PropertyMapper nestedMapper = entry.getValue(); + if ( nestedMapper instanceof CompositeMapperBuilder ) { + addMiddleEqualToQuery( (CompositeMapperBuilder) nestedMapper, parameters, idPrefix1, prefix1, idPrefix2, prefix2 ); + } + else if ( nestedMapper instanceof ToOneIdMapper ) { + ( (ToOneIdMapper) nestedMapper ).addMiddleEqualToQuery( parameters, idPrefix1, prefix1, idPrefix2, prefix2 ); + } + else { + parameters.addWhere( prefix1 + '.' + propertyName, false, "=", prefix2 + '.' + propertyName, false ); + } + } + } + + @Override + public CompositeMapperBuilder addComponent(PropertyData propertyData, String componentClassName) { + return delegate.addComponent( propertyData, componentClassName ); + } + + @Override + public void addComposite(PropertyData propertyData, PropertyMapper propertyMapper) { + delegate.addComposite( propertyData, propertyMapper ); + } + + @Override + public void add(PropertyData propertyData) { + delegate.add( propertyData ); + } + + public Map getProperties() { + return delegate.getProperties(); + } +} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleMapKeyIdComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleMapKeyIdComponentMapper.java index b0b48b8155..464f9a2c24 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleMapKeyIdComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleMapKeyIdComponentMapper.java @@ -24,6 +24,7 @@ package org.hibernate.envers.entities.mapper.relation.component; import java.util.Map; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.envers.configuration.AuditEntitiesConfiguration; import org.hibernate.envers.entities.EntityInstantiator; import org.hibernate.envers.entities.mapper.id.IdMapper; @@ -49,11 +50,11 @@ public Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map< return relatedIdMapper.mapToIdFromMap((Map) data.get(verEntCfg.getOriginalIdPropName())); } - public void mapToMapFromObject(Map data, Object obj) { + public void mapToMapFromObject(SessionImplementor session, Map idData, Map data, Object obj) { // Doing nothing. } - public void addMiddleEqualToQuery(Parameters parameters, String prefix1, String prefix2) { + public void addMiddleEqualToQuery(Parameters parameters, String idPrefix1, String prefix1, String idPrefix2, String prefix2) { // Doing nothing. } } \ No newline at end of file diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleMapKeyPropertyComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleMapKeyPropertyComponentMapper.java index 7739976397..f619e01ae7 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleMapKeyPropertyComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleMapKeyPropertyComponentMapper.java @@ -24,6 +24,7 @@ package org.hibernate.envers.entities.mapper.relation.component; import java.util.Map; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.envers.entities.EntityInstantiator; import org.hibernate.envers.tools.query.Parameters; import org.hibernate.envers.tools.reflection.ReflectionTools; @@ -49,11 +50,11 @@ public Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map< return ReflectionTools.getGetter(dataObject.getClass(), propertyName, accessType).get(dataObject); } - public void mapToMapFromObject(Map data, Object obj) { + public void mapToMapFromObject(SessionImplementor session, Map idData, Map data, Object obj) { // Doing nothing. } - public void addMiddleEqualToQuery(Parameters parameters, String prefix1, String prefix2) { + public void addMiddleEqualToQuery(Parameters parameters, String idPrefix1, String prefix1, String idPrefix2, String prefix2) { // Doing nothing. } } \ No newline at end of file diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleRelatedComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleRelatedComponentMapper.java index 40de4daa18..fb43169c9b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleRelatedComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleRelatedComponentMapper.java @@ -24,6 +24,7 @@ package org.hibernate.envers.entities.mapper.relation.component; import java.util.Map; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.envers.entities.EntityInstantiator; import org.hibernate.envers.entities.mapper.relation.MiddleIdData; import org.hibernate.envers.tools.query.Parameters; @@ -43,11 +44,11 @@ public Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map< return entityInstantiator.createInstanceFromVersionsEntity(relatedIdData.getEntityName(), data, revision); } - public void mapToMapFromObject(Map data, Object obj) { - relatedIdData.getPrefixedMapper().mapToMapFromEntity(data, obj); + public void mapToMapFromObject(SessionImplementor session, Map idData, Map data, Object obj) { + relatedIdData.getPrefixedMapper().mapToMapFromEntity(idData, obj); } - public void addMiddleEqualToQuery(Parameters parameters, String prefix1, String prefix2) { - relatedIdData.getPrefixedMapper().addIdsEqualToQuery(parameters, prefix1, prefix2); + public void addMiddleEqualToQuery(Parameters parameters, String idPrefix1, String prefix1, String idPrefix2, String prefix2) { + relatedIdData.getPrefixedMapper().addIdsEqualToQuery(parameters, idPrefix1, idPrefix2); } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleSimpleComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleSimpleComponentMapper.java index 3cf72b97c6..f6cc572943 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleSimpleComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleSimpleComponentMapper.java @@ -24,6 +24,7 @@ package org.hibernate.envers.entities.mapper.relation.component; import java.util.Map; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.envers.configuration.AuditEntitiesConfiguration; import org.hibernate.envers.entities.EntityInstantiator; import org.hibernate.envers.tools.query.Parameters; @@ -46,11 +47,11 @@ public Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map< return ((Map) data.get(verEntCfg.getOriginalIdPropName())).get(propertyName); } - public void mapToMapFromObject(Map data, Object obj) { - data.put(propertyName, obj); + public void mapToMapFromObject(SessionImplementor session, Map idData, Map data, Object obj) { + idData.put(propertyName, obj); } - public void addMiddleEqualToQuery(Parameters parameters, String prefix1, String prefix2) { - parameters.addWhere(prefix1 + "." + propertyName, false, "=", prefix2 + "." + propertyName, false); + public void addMiddleEqualToQuery(Parameters parameters, String idPrefix1, String prefix1, String idPrefix2, String prefix2) { + parameters.addWhere(idPrefix1 + "." + propertyName, false, "=", idPrefix2 + "." + propertyName, false); } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleStraightComponentMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleStraightComponentMapper.java index 049b6ca960..30fdd16535 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleStraightComponentMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/component/MiddleStraightComponentMapper.java @@ -24,6 +24,7 @@ package org.hibernate.envers.entities.mapper.relation.component; import java.util.Map; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.envers.entities.EntityInstantiator; import org.hibernate.envers.tools.query.Parameters; @@ -45,11 +46,11 @@ public Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map< return data.get(propertyName); } - public void mapToMapFromObject(Map data, Object obj) { - data.put(propertyName, obj); - } + public void mapToMapFromObject(SessionImplementor session, Map idData, Map data, Object obj) { + idData.put(propertyName, obj); + } - public void addMiddleEqualToQuery(Parameters parameters, String prefix1, String prefix2) { + public void addMiddleEqualToQuery(Parameters parameters, String idPrefix1, String prefix1, String idPrefix2, String prefix2) { throw new UnsupportedOperationException("Cannot use this mapper with a middle table!"); } } \ No newline at end of file diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/AbstractRelationQueryGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/AbstractRelationQueryGenerator.java new file mode 100644 index 0000000000..cb3fe4a696 --- /dev/null +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/AbstractRelationQueryGenerator.java @@ -0,0 +1,70 @@ +/* + * 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.entities.mapper.relation.query; + +import org.hibernate.Query; +import org.hibernate.envers.RevisionType; +import org.hibernate.envers.configuration.AuditEntitiesConfiguration; +import org.hibernate.envers.entities.mapper.id.QueryParameterData; +import org.hibernate.envers.entities.mapper.relation.MiddleIdData; +import org.hibernate.envers.reader.AuditReaderImplementor; + +import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.DEL_REVISION_TYPE_PARAMETER; +import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.REVISION_PARAMETER; + +/** + * Base class for implementers of {@code RelationQueryGenerator} contract. + * + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +public abstract class AbstractRelationQueryGenerator implements RelationQueryGenerator { + protected final AuditEntitiesConfiguration verEntCfg; + protected final MiddleIdData referencingIdData; + protected final boolean revisionTypeInId; + + protected AbstractRelationQueryGenerator(AuditEntitiesConfiguration verEntCfg, MiddleIdData referencingIdData, + boolean revisionTypeInId) { + this.verEntCfg = verEntCfg; + this.referencingIdData = referencingIdData; + this.revisionTypeInId = revisionTypeInId; + } + + protected abstract String getQueryString(); + + public Query getQuery(AuditReaderImplementor versionsReader, Object primaryKey, Number revision) { + Query query = versionsReader.getSession().createQuery( getQueryString() ); + query.setParameter( REVISION_PARAMETER, revision ); + query.setParameter( DEL_REVISION_TYPE_PARAMETER, RevisionType.DEL ); + for ( QueryParameterData paramData : referencingIdData.getPrefixedMapper().mapToQueryParametersFromId( primaryKey ) ) { + paramData.setParameterValue( query ); + } + return query; + } + + protected String getRevisionTypePath() { + return revisionTypeInId + ? verEntCfg.getOriginalIdPropName() + "." + verEntCfg.getRevisionTypePropName() + : verEntCfg.getRevisionTypePropName(); + } +} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/OneAuditEntityQueryGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/OneAuditEntityQueryGenerator.java index 9d92fcbe8a..953be5ee0d 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/OneAuditEntityQueryGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/OneAuditEntityQueryGenerator.java @@ -45,15 +45,15 @@ * Selects data from an audit entity. * @author Adam Warski (adam at warski dot org) */ -public final class OneAuditEntityQueryGenerator implements RelationQueryGenerator { +public final class OneAuditEntityQueryGenerator extends AbstractRelationQueryGenerator { private final String queryString; - private final MiddleIdData referencingIdData; public OneAuditEntityQueryGenerator(GlobalConfiguration globalCfg, AuditEntitiesConfiguration verEntCfg, AuditStrategy auditStrategy, MiddleIdData referencingIdData, - String referencedEntityName, MiddleIdData referencedIdData) { - this.referencingIdData = referencingIdData; + String referencedEntityName, MiddleIdData referencedIdData, + boolean revisionTypeInId) { + super( verEntCfg, referencingIdData, revisionTypeInId ); /* * The query that we need to create: @@ -93,21 +93,15 @@ public OneAuditEntityQueryGenerator(GlobalConfiguration globalCfg, AuditEntities revisionPropertyPath, originalIdPropertyName, REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR); // e.revision_type != DEL - rootParameters.addWhereWithNamedParam(verEntCfg.getRevisionTypePropName(), false, "!=", DEL_REVISION_TYPE_PARAMETER); + rootParameters.addWhereWithNamedParam(getRevisionTypePath(), false, "!=", DEL_REVISION_TYPE_PARAMETER); StringBuilder sb = new StringBuilder(); qb.build(sb, Collections.emptyMap()); queryString = sb.toString(); } - public Query getQuery(AuditReaderImplementor versionsReader, Object primaryKey, Number revision) { - Query query = versionsReader.getSession().createQuery(queryString); - query.setParameter(REVISION_PARAMETER, revision); - query.setParameter(DEL_REVISION_TYPE_PARAMETER, RevisionType.DEL); - for (QueryParameterData paramData: referencingIdData.getPrefixedMapper().mapToQueryParametersFromId(primaryKey)) { - paramData.setParameterValue(query); - } - - return query; - } + @Override + protected String getQueryString() { + return queryString; + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/OneEntityQueryGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/OneEntityQueryGenerator.java index cda7105467..45d8e47728 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/OneEntityQueryGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/OneEntityQueryGenerator.java @@ -44,16 +44,16 @@ * Selects data from a relation middle-table only. * @author Adam Warski (adam at warski dot org) */ -public final class OneEntityQueryGenerator implements RelationQueryGenerator { +public final class OneEntityQueryGenerator extends AbstractRelationQueryGenerator { private final String queryString; - private final MiddleIdData referencingIdData; public OneEntityQueryGenerator(AuditEntitiesConfiguration verEntCfg, AuditStrategy auditStrategy, String versionsMiddleEntityName, MiddleIdData referencingIdData, + boolean revisionTypeInId, MiddleComponentData... componentDatas) { - this.referencingIdData = referencingIdData; + super( verEntCfg, referencingIdData, revisionTypeInId ); /* * The query that we need to create: @@ -90,25 +90,19 @@ public OneEntityQueryGenerator(AuditEntitiesConfiguration verEntCfg, // (with ee association at revision :revision) // --> based on auditStrategy (see above) auditStrategy.addAssociationAtRevisionRestriction(qb, revisionPropertyPath, - verEntCfg.getRevisionEndFieldName(), true,referencingIdData, versionsMiddleEntityName, - eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, componentDatas); + verEntCfg.getRevisionEndFieldName(), true, referencingIdData, versionsMiddleEntityName, + eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, MIDDLE_ENTITY_ALIAS, componentDatas); // ee.revision_type != DEL - rootParameters.addWhereWithNamedParam(verEntCfg.getRevisionTypePropName(), "!=", DEL_REVISION_TYPE_PARAMETER); + rootParameters.addWhereWithNamedParam(getRevisionTypePath(), "!=", DEL_REVISION_TYPE_PARAMETER); StringBuilder sb = new StringBuilder(); qb.build(sb, Collections.emptyMap()); queryString = sb.toString(); } - public Query getQuery(AuditReaderImplementor versionsReader, Object primaryKey, Number revision) { - Query query = versionsReader.getSession().createQuery(queryString); - query.setParameter(REVISION_PARAMETER, revision); - query.setParameter(DEL_REVISION_TYPE_PARAMETER, RevisionType.DEL); - for (QueryParameterData paramData: referencingIdData.getPrefixedMapper().mapToQueryParametersFromId(primaryKey)) { - paramData.setParameterValue(query); - } - - return query; - } + @Override + protected String getQueryString() { + return queryString; + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/ThreeEntityQueryGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/ThreeEntityQueryGenerator.java index 0a13260127..782d12cea3 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/ThreeEntityQueryGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/ThreeEntityQueryGenerator.java @@ -49,9 +49,8 @@ * Selects data from a relation middle-table and a two related versions entity. * @author Adam Warski (adam at warski dot org) */ -public final class ThreeEntityQueryGenerator implements RelationQueryGenerator { +public final class ThreeEntityQueryGenerator extends AbstractRelationQueryGenerator { private final String queryString; - private final MiddleIdData referencingIdData; public ThreeEntityQueryGenerator(GlobalConfiguration globalCfg, AuditEntitiesConfiguration verEntCfg, @@ -60,8 +59,9 @@ public ThreeEntityQueryGenerator(GlobalConfiguration globalCfg, MiddleIdData referencingIdData, MiddleIdData referencedIdData, MiddleIdData indexIdData, + boolean revisionTypeInId, MiddleComponentData... componentDatas) { - this.referencingIdData = referencingIdData; + super( verEntCfg, referencingIdData, revisionTypeInId ); /* * The query that we need to create: @@ -157,28 +157,23 @@ public ThreeEntityQueryGenerator(GlobalConfiguration globalCfg, // --> based on auditStrategy (see above) auditStrategy.addAssociationAtRevisionRestriction(qb, revisionPropertyPath, verEntCfg.getRevisionEndFieldName(), true, referencingIdData, versionsMiddleEntityName, - eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, componentDatas); + eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, MIDDLE_ENTITY_ALIAS, componentDatas); // ee.revision_type != DEL - rootParameters.addWhereWithNamedParam(verEntCfg.getRevisionTypePropName(), "!=", DEL_REVISION_TYPE_PARAMETER); + String revisionTypePropName = getRevisionTypePath(); + rootParameters.addWhereWithNamedParam(revisionTypePropName, "!=", DEL_REVISION_TYPE_PARAMETER); // e.revision_type != DEL - rootParameters.addWhereWithNamedParam(REFERENCED_ENTITY_ALIAS + "." + verEntCfg.getRevisionTypePropName(), false, "!=", DEL_REVISION_TYPE_PARAMETER); + rootParameters.addWhereWithNamedParam(REFERENCED_ENTITY_ALIAS + "." + revisionTypePropName, false, "!=", DEL_REVISION_TYPE_PARAMETER); // f.revision_type != DEL - rootParameters.addWhereWithNamedParam(INDEX_ENTITY_ALIAS + "." + verEntCfg.getRevisionTypePropName(), false, "!=", DEL_REVISION_TYPE_PARAMETER); + rootParameters.addWhereWithNamedParam(INDEX_ENTITY_ALIAS + "." + revisionTypePropName, false, "!=", DEL_REVISION_TYPE_PARAMETER); StringBuilder sb = new StringBuilder(); qb.build(sb, Collections.emptyMap()); queryString = sb.toString(); } - public Query getQuery(AuditReaderImplementor versionsReader, Object primaryKey, Number revision) { - Query query = versionsReader.getSession().createQuery(queryString); - query.setParameter(REVISION_PARAMETER, revision); - query.setParameter(DEL_REVISION_TYPE_PARAMETER, RevisionType.DEL); - for (QueryParameterData paramData: referencingIdData.getPrefixedMapper().mapToQueryParametersFromId(primaryKey)) { - paramData.setParameterValue(query); - } - - return query; - } + @Override + protected String getQueryString() { + return queryString; + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/TwoEntityOneAuditedQueryGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/TwoEntityOneAuditedQueryGenerator.java index 13ddb92247..d5402cd5cd 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/TwoEntityOneAuditedQueryGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/TwoEntityOneAuditedQueryGenerator.java @@ -45,16 +45,16 @@ * Selects data from a relation middle-table and a related non-audited entity. * @author Adam Warski (adam at warski dot org) */ -public final class TwoEntityOneAuditedQueryGenerator implements RelationQueryGenerator { +public final class TwoEntityOneAuditedQueryGenerator extends AbstractRelationQueryGenerator { private final String queryString; - private final MiddleIdData referencingIdData; public TwoEntityOneAuditedQueryGenerator(AuditEntitiesConfiguration verEntCfg, AuditStrategy auditStrategy, String versionsMiddleEntityName, MiddleIdData referencingIdData, MiddleIdData referencedIdData, + boolean revisionTypeInId, MiddleComponentData... componentDatas) { - this.referencingIdData = referencingIdData; + super( verEntCfg, referencingIdData, revisionTypeInId ); /* * The query that we need to create: @@ -99,24 +99,18 @@ public TwoEntityOneAuditedQueryGenerator(AuditEntitiesConfiguration verEntCfg, A // --> based on auditStrategy (see above) auditStrategy.addAssociationAtRevisionRestriction(qb, revisionPropertyPath, verEntCfg.getRevisionEndFieldName(), true,referencingIdData, versionsMiddleEntityName, - eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, componentDatas); + eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, MIDDLE_ENTITY_ALIAS, componentDatas); // ee.revision_type != DEL - rootParameters.addWhereWithNamedParam(verEntCfg.getRevisionTypePropName(), "!=", DEL_REVISION_TYPE_PARAMETER); + rootParameters.addWhereWithNamedParam(getRevisionTypePath(), "!=", DEL_REVISION_TYPE_PARAMETER); StringBuilder sb = new StringBuilder(); qb.build(sb, Collections.emptyMap()); queryString = sb.toString(); } - public Query getQuery(AuditReaderImplementor versionsReader, Object primaryKey, Number revision) { - Query query = versionsReader.getSession().createQuery(queryString); - query.setParameter(REVISION_PARAMETER, revision); - query.setParameter(DEL_REVISION_TYPE_PARAMETER, RevisionType.DEL); - for (QueryParameterData paramData: referencingIdData.getPrefixedMapper().mapToQueryParametersFromId(primaryKey)) { - paramData.setParameterValue(query); - } - - return query; - } + @Override + protected String getQueryString() { + return queryString; + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/TwoEntityQueryGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/TwoEntityQueryGenerator.java index aa5f5292f9..42642bd7c6 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/TwoEntityQueryGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/query/TwoEntityQueryGenerator.java @@ -47,9 +47,8 @@ * Selects data from a relation middle-table and a related versions entity. * @author Adam Warski (adam at warski dot org) */ -public final class TwoEntityQueryGenerator implements RelationQueryGenerator { +public final class TwoEntityQueryGenerator extends AbstractRelationQueryGenerator { private final String queryString; - private final MiddleIdData referencingIdData; public TwoEntityQueryGenerator(GlobalConfiguration globalCfg, AuditEntitiesConfiguration verEntCfg, @@ -57,8 +56,9 @@ public TwoEntityQueryGenerator(GlobalConfiguration globalCfg, String versionsMiddleEntityName, MiddleIdData referencingIdData, MiddleIdData referencedIdData, + boolean revisionTypeInId, MiddleComponentData... componentDatas) { - this.referencingIdData = referencingIdData; + super( verEntCfg, referencingIdData, revisionTypeInId ); /* * The query that we need to create: @@ -118,26 +118,21 @@ public TwoEntityQueryGenerator(GlobalConfiguration globalCfg, // --> based on auditStrategy (see above) auditStrategy.addAssociationAtRevisionRestriction(qb, revisionPropertyPath, verEntCfg.getRevisionEndFieldName(), true, referencingIdData, versionsMiddleEntityName, - eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, componentDatas); + eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, MIDDLE_ENTITY_ALIAS, componentDatas); // ee.revision_type != DEL - rootParameters.addWhereWithNamedParam(verEntCfg.getRevisionTypePropName(), "!=", DEL_REVISION_TYPE_PARAMETER); + String revisionTypePropName = getRevisionTypePath(); + rootParameters.addWhereWithNamedParam(revisionTypePropName, "!=", DEL_REVISION_TYPE_PARAMETER); // e.revision_type != DEL - rootParameters.addWhereWithNamedParam(REFERENCED_ENTITY_ALIAS + "." + verEntCfg.getRevisionTypePropName(), false, "!=", DEL_REVISION_TYPE_PARAMETER); + rootParameters.addWhereWithNamedParam(REFERENCED_ENTITY_ALIAS + "." + revisionTypePropName, false, "!=", DEL_REVISION_TYPE_PARAMETER); StringBuilder sb = new StringBuilder(); qb.build(sb, Collections.emptyMap()); queryString = sb.toString(); } - public Query getQuery(AuditReaderImplementor versionsReader, Object primaryKey, Number revision) { - Query query = versionsReader.getSession().createQuery(queryString); - query.setParameter(REVISION_PARAMETER, revision); - query.setParameter(DEL_REVISION_TYPE_PARAMETER, RevisionType.DEL); - for (QueryParameterData paramData: referencingIdData.getPrefixedMapper().mapToQueryParametersFromId(primaryKey)) { - paramData.setParameterValue(query); - } - - return query; - } + @Override + protected String getQueryString() { + return queryString; + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/event/BaseEnversCollectionEventListener.java b/hibernate-envers/src/main/java/org/hibernate/envers/event/BaseEnversCollectionEventListener.java index c79a3c6134..b6ee70c6de 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/event/BaseEnversCollectionEventListener.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/event/BaseEnversCollectionEventListener.java @@ -156,7 +156,7 @@ private void generateFakeBidirecationalRelationWorkUnits( .getEntCfg() .get( collectionEntityName ) .getPropertyMapper() - .mapCollectionChanges( referencingPropertyName, newColl, oldColl, event.getAffectedOwnerIdOrNull() ); + .mapCollectionChanges( event.getSession(), referencingPropertyName, newColl, oldColl, event.getAffectedOwnerIdOrNull() ); // Getting the id mapper for the related entity, as the work units generated will corrspond to the related // entities. diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/AuditStrategy.java b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/AuditStrategy.java index 16fcc14656..3a84d1ecb3 100755 --- a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/AuditStrategy.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/AuditStrategy.java @@ -1,99 +1,104 @@ -package org.hibernate.envers.strategy; -import java.io.Serializable; - -import org.hibernate.Session; -import org.hibernate.envers.configuration.AuditConfiguration; -import org.hibernate.envers.configuration.GlobalConfiguration; -import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData; -import org.hibernate.envers.entities.mapper.relation.MiddleComponentData; -import org.hibernate.envers.entities.mapper.relation.MiddleIdData; -import org.hibernate.envers.tools.query.QueryBuilder; - -/** - * Behaviours of different audit strategy for populating audit data. - * - * @author Stephanie Pau - * @author Adam Warski (adam at warski dot org) - */ -public interface AuditStrategy { - /** - * Perform the persistence of audited data for regular entities. - * - * @param session Session, which can be used to persist the data. - * @param entityName Name of the entity, in which the audited change happens - * @param auditCfg Audit configuration - * @param id Id of the entity. - * @param data Audit data to persist - * @param revision Current revision data - */ - void perform(Session session, String entityName, AuditConfiguration auditCfg, Serializable id, Object data, - Object revision); - - /** - * Perform the persistence of audited data for collection ("middle") entities. - * - * @param session Session, which can be used to persist the data. - * @param auditCfg Audit configuration - * @param persistentCollectionChangeData Collection change data to be persisted. - * @param revision Current revision data - */ - void performCollectionChange(Session session, AuditConfiguration auditCfg, - PersistentCollectionChangeData persistentCollectionChangeData, Object revision); - - - /** - * Update the rootQueryBuilder with an extra WHERE clause to restrict the revision for a two-entity relation. - * This WHERE clause depends on the AuditStrategy, as follows: - *
    - *
  • For {@link DefaultAuditStrategy} a subquery is created: - *

    e.revision = (SELECT max(...) ...)

    - *
  • - *
  • for {@link ValidityAuditStrategy} the revision-end column is used: - *

    e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)

    - *
  • - *
- * - * @param globalCfg the {@link GlobalConfiguration} - * @param rootQueryBuilder the {@link QueryBuilder} that will be updated - * @param revisionProperty property of the revision column - * @param revisionEndProperty property of the revisionEnd column (only used for {@link ValidityAuditStrategy}) - * @param addAlias {@code boolean} indicator if a left alias is needed - * @param idData id-information for the two-entity relation (only used for {@link DefaultAuditStrategy}) - * @param revisionPropertyPath path of the revision property (only used for {@link ValidityAuditStrategy}) - * @param originalIdPropertyName name of the id property (only used for {@link ValidityAuditStrategy}) - * @param alias1 an alias used for subquery (only used for {@link ValidityAuditStrategy}) - * @param alias2 an alias used for subquery (only used for {@link ValidityAuditStrategy}) - */ - void addEntityAtRevisionRestriction(GlobalConfiguration globalCfg, QueryBuilder rootQueryBuilder, - String revisionProperty, String revisionEndProperty, boolean addAlias, MiddleIdData idData, - String revisionPropertyPath, String originalIdPropertyName, String alias1, String alias2); - - /** - * Update the rootQueryBuilder with an extra WHERE clause to restrict the revision for a middle-entity - * association. This WHERE clause depends on the AuditStrategy, as follows: - *
    - *
  • For {@link DefaultAuditStrategy} a subquery is created: - *

    e.revision = (SELECT max(...) ...)

    - *
  • - *
  • for {@link ValidityAuditStrategy} the revision-end column is used: - *

    e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)

    - *
  • - *
- * - * @param rootQueryBuilder the {@link QueryBuilder} that will be updated - * @param revisionProperty property of the revision column - * @param revisionEndProperty property of the revisionEnd column (only used for {@link ValidityAuditStrategy}) - * @param addAlias {@code boolean} indicator if a left alias is needed - * @param referencingIdData id-information for the middle-entity association (only used for {@link DefaultAuditStrategy}) - * @param versionsMiddleEntityName name of the middle-entity - * @param eeOriginalIdPropertyPath name of the id property (only used for {@link ValidityAuditStrategy}) - * @param revisionPropertyPath path of the revision property (only used for {@link ValidityAuditStrategy}) - * @param originalIdPropertyName name of the id property (only used for {@link ValidityAuditStrategy}) - * @param componentDatas information about the middle-entity relation - */ - void addAssociationAtRevisionRestriction(QueryBuilder rootQueryBuilder, String revisionProperty, - String revisionEndProperty, boolean addAlias, MiddleIdData referencingIdData, - String versionsMiddleEntityName, String eeOriginalIdPropertyPath, String revisionPropertyPath, - String originalIdPropertyName, MiddleComponentData... componentDatas); - -} +package org.hibernate.envers.strategy; + +import java.io.Serializable; + +import org.hibernate.Session; +import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.envers.configuration.AuditConfiguration; +import org.hibernate.envers.configuration.GlobalConfiguration; +import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData; +import org.hibernate.envers.entities.mapper.relation.MiddleComponentData; +import org.hibernate.envers.entities.mapper.relation.MiddleIdData; +import org.hibernate.envers.tools.query.QueryBuilder; + +/** + * Behaviours of different audit strategy for populating audit data. + * + * @author Stephanie Pau + * @author Adam Warski (adam at warski dot org) + */ +public interface AuditStrategy { + /** + * Perform the persistence of audited data for regular entities. + * + * @param session Session, which can be used to persist the data. + * @param entityName Name of the entity, in which the audited change happens + * @param auditCfg Audit configuration + * @param id Id of the entity. + * @param data Audit data to persist + * @param revision Current revision data + */ + void perform(Session session, String entityName, AuditConfiguration auditCfg, Serializable id, Object data, + Object revision); + + /** + * Perform the persistence of audited data for collection ("middle") entities. + * + * @param session Session, which can be used to persist the data. + * @param entityName Name of the entity, in which the audited change happens. + * @param propertyName The name of the property holding the {@link PersistentCollection}. + * @param auditCfg Audit configuration + * @param persistentCollectionChangeData Collection change data to be persisted. + * @param revision Current revision data + */ + void performCollectionChange(Session session, String entityName, String propertyName, AuditConfiguration auditCfg, + PersistentCollectionChangeData persistentCollectionChangeData, Object revision); + + + /** + * Update the rootQueryBuilder with an extra WHERE clause to restrict the revision for a two-entity relation. + * This WHERE clause depends on the AuditStrategy, as follows: + *
    + *
  • For {@link DefaultAuditStrategy} a subquery is created: + *

    e.revision = (SELECT max(...) ...)

    + *
  • + *
  • for {@link ValidityAuditStrategy} the revision-end column is used: + *

    e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)

    + *
  • + *
+ * + * @param globalCfg the {@link GlobalConfiguration} + * @param rootQueryBuilder the {@link QueryBuilder} that will be updated + * @param revisionProperty property of the revision column + * @param revisionEndProperty property of the revisionEnd column (only used for {@link ValidityAuditStrategy}) + * @param addAlias {@code boolean} indicator if a left alias is needed + * @param idData id-information for the two-entity relation (only used for {@link DefaultAuditStrategy}) + * @param revisionPropertyPath path of the revision property (only used for {@link ValidityAuditStrategy}) + * @param originalIdPropertyName name of the id property (only used for {@link ValidityAuditStrategy}) + * @param alias1 an alias used for subquery (only used for {@link ValidityAuditStrategy}) + * @param alias2 an alias used for subquery (only used for {@link ValidityAuditStrategy}) + */ + void addEntityAtRevisionRestriction(GlobalConfiguration globalCfg, QueryBuilder rootQueryBuilder, + String revisionProperty, String revisionEndProperty, boolean addAlias, MiddleIdData idData, + String revisionPropertyPath, String originalIdPropertyName, String alias1, String alias2); + + /** + * Update the rootQueryBuilder with an extra WHERE clause to restrict the revision for a middle-entity + * association. This WHERE clause depends on the AuditStrategy, as follows: + *
    + *
  • For {@link DefaultAuditStrategy} a subquery is created: + *

    e.revision = (SELECT max(...) ...)

    + *
  • + *
  • for {@link ValidityAuditStrategy} the revision-end column is used: + *

    e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)

    + *
  • + *
+ * + * @param rootQueryBuilder the {@link QueryBuilder} that will be updated + * @param revisionProperty property of the revision column + * @param revisionEndProperty property of the revisionEnd column (only used for {@link ValidityAuditStrategy}) + * @param addAlias {@code boolean} indicator if a left alias is needed + * @param referencingIdData id-information for the middle-entity association (only used for {@link DefaultAuditStrategy}) + * @param versionsMiddleEntityName name of the middle-entity + * @param eeOriginalIdPropertyPath name of the id property (only used for {@link ValidityAuditStrategy}) + * @param revisionPropertyPath path of the revision property (only used for {@link ValidityAuditStrategy}) + * @param originalIdPropertyName name of the id property (only used for {@link ValidityAuditStrategy}) + * @param alias1 an alias used for subqueries (only used for {@link DefaultAuditStrategy}) + * @param componentDatas information about the middle-entity relation + */ + void addAssociationAtRevisionRestriction(QueryBuilder rootQueryBuilder, String revisionProperty, + String revisionEndProperty, boolean addAlias, MiddleIdData referencingIdData, + String versionsMiddleEntityName, String eeOriginalIdPropertyPath, String revisionPropertyPath, + String originalIdPropertyName, String alias1, MiddleComponentData... componentDatas); + +} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/DefaultAuditStrategy.java b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/DefaultAuditStrategy.java index 12693229ef..5959c75d94 100755 --- a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/DefaultAuditStrategy.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/DefaultAuditStrategy.java @@ -1,90 +1,90 @@ -package org.hibernate.envers.strategy; - -import java.io.Serializable; - -import org.hibernate.Session; -import org.hibernate.envers.configuration.AuditConfiguration; -import org.hibernate.envers.configuration.GlobalConfiguration; -import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData; -import org.hibernate.envers.entities.mapper.relation.MiddleComponentData; -import org.hibernate.envers.entities.mapper.relation.MiddleIdData; -import org.hibernate.envers.synchronization.SessionCacheCleaner; -import org.hibernate.envers.tools.query.Parameters; -import org.hibernate.envers.tools.query.QueryBuilder; - -import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.MIDDLE_ENTITY_ALIAS_DEF_AUD_STR; -import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.REVISION_PARAMETER; - -/** - * Default strategy is to simply persist the audit data. - * - * @author Adam Warski - * @author Stephanie Pau - */ -public class DefaultAuditStrategy implements AuditStrategy { - private final SessionCacheCleaner sessionCacheCleaner; - - public DefaultAuditStrategy() { - sessionCacheCleaner = new SessionCacheCleaner(); - } - - public void perform(Session session, String entityName, AuditConfiguration auditCfg, Serializable id, Object data, - Object revision) { - session.save(auditCfg.getAuditEntCfg().getAuditEntityName(entityName), data); - sessionCacheCleaner.scheduleAuditDataRemoval(session, data); - } - - public void performCollectionChange(Session session, AuditConfiguration auditCfg, - PersistentCollectionChangeData persistentCollectionChangeData, Object revision) { - session.save(persistentCollectionChangeData.getEntityName(), persistentCollectionChangeData.getData()); - sessionCacheCleaner.scheduleAuditDataRemoval(session, persistentCollectionChangeData.getData()); - } - - - public void addEntityAtRevisionRestriction(GlobalConfiguration globalCfg, QueryBuilder rootQueryBuilder, String revisionProperty, - String revisionEndProperty, boolean addAlias, MiddleIdData idData, String revisionPropertyPath, - String originalIdPropertyName, String alias1, String alias2) { - Parameters rootParameters = rootQueryBuilder.getRootParameters(); - - // create a subquery builder - // SELECT max(e.revision) FROM versionsReferencedEntity e2 - QueryBuilder maxERevQb = rootQueryBuilder.newSubQueryBuilder(idData.getAuditEntityName(), alias2); - maxERevQb.addProjection("max", revisionPropertyPath, false); - // WHERE - Parameters maxERevQbParameters = maxERevQb.getRootParameters(); - // e2.revision <= :revision - maxERevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", REVISION_PARAMETER); - // e2.id_ref_ed = e.id_ref_ed - idData.getOriginalMapper().addIdsEqualToQuery(maxERevQbParameters, - alias1 + "." + originalIdPropertyName, alias2 +"." + originalIdPropertyName); - - // add subquery to rootParameters - String subqueryOperator = globalCfg.getCorrelatedSubqueryOperator(); - rootParameters.addWhere(revisionProperty, addAlias, subqueryOperator, maxERevQb); - } - - public void addAssociationAtRevisionRestriction(QueryBuilder rootQueryBuilder, String revisionProperty, - String revisionEndProperty, boolean addAlias, MiddleIdData referencingIdData, String versionsMiddleEntityName, - String eeOriginalIdPropertyPath, String revisionPropertyPath, - String originalIdPropertyName, MiddleComponentData... componentDatas) { - Parameters rootParameters = rootQueryBuilder.getRootParameters(); - - // SELECT max(ee2.revision) FROM middleEntity ee2 - QueryBuilder maxEeRevQb = rootQueryBuilder.newSubQueryBuilder(versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS_DEF_AUD_STR); - maxEeRevQb.addProjection("max", revisionPropertyPath, false); - // WHERE - Parameters maxEeRevQbParameters = maxEeRevQb.getRootParameters(); - // ee2.revision <= :revision - maxEeRevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", REVISION_PARAMETER); - // ee2.originalId.* = ee.originalId.* - String ee2OriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS_DEF_AUD_STR + "." + originalIdPropertyName; - referencingIdData.getPrefixedMapper().addIdsEqualToQuery(maxEeRevQbParameters, eeOriginalIdPropertyPath, ee2OriginalIdPropertyPath); - for (MiddleComponentData componentData : componentDatas) { - componentData.getComponentMapper().addMiddleEqualToQuery(maxEeRevQbParameters, eeOriginalIdPropertyPath, ee2OriginalIdPropertyPath); - } - - // add subquery to rootParameters - rootParameters.addWhere(revisionProperty, addAlias, "=", maxEeRevQb); - } - -} +package org.hibernate.envers.strategy; + +import java.io.Serializable; + +import org.hibernate.Session; +import org.hibernate.envers.configuration.AuditConfiguration; +import org.hibernate.envers.configuration.GlobalConfiguration; +import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData; +import org.hibernate.envers.entities.mapper.relation.MiddleComponentData; +import org.hibernate.envers.entities.mapper.relation.MiddleIdData; +import org.hibernate.envers.synchronization.SessionCacheCleaner; +import org.hibernate.envers.tools.query.Parameters; +import org.hibernate.envers.tools.query.QueryBuilder; + +import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.MIDDLE_ENTITY_ALIAS_DEF_AUD_STR; +import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants.REVISION_PARAMETER; + +/** + * Default strategy is to simply persist the audit data. + * + * @author Adam Warski + * @author Stephanie Pau + */ +public class DefaultAuditStrategy implements AuditStrategy { + private final SessionCacheCleaner sessionCacheCleaner; + + public DefaultAuditStrategy() { + sessionCacheCleaner = new SessionCacheCleaner(); + } + + public void perform(Session session, String entityName, AuditConfiguration auditCfg, Serializable id, Object data, + Object revision) { + session.save(auditCfg.getAuditEntCfg().getAuditEntityName(entityName), data); + sessionCacheCleaner.scheduleAuditDataRemoval(session, data); + } + + public void performCollectionChange(Session session, String entityName, String propertyName, AuditConfiguration auditCfg, + PersistentCollectionChangeData persistentCollectionChangeData, Object revision) { + session.save(persistentCollectionChangeData.getEntityName(), persistentCollectionChangeData.getData()); + sessionCacheCleaner.scheduleAuditDataRemoval(session, persistentCollectionChangeData.getData()); + } + + + public void addEntityAtRevisionRestriction(GlobalConfiguration globalCfg, QueryBuilder rootQueryBuilder, String revisionProperty, + String revisionEndProperty, boolean addAlias, MiddleIdData idData, String revisionPropertyPath, + String originalIdPropertyName, String alias1, String alias2) { + Parameters rootParameters = rootQueryBuilder.getRootParameters(); + + // create a subquery builder + // SELECT max(e.revision) FROM versionsReferencedEntity e2 + QueryBuilder maxERevQb = rootQueryBuilder.newSubQueryBuilder(idData.getAuditEntityName(), alias2); + maxERevQb.addProjection("max", revisionPropertyPath, false); + // WHERE + Parameters maxERevQbParameters = maxERevQb.getRootParameters(); + // e2.revision <= :revision + maxERevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", REVISION_PARAMETER); + // e2.id_ref_ed = e.id_ref_ed + idData.getOriginalMapper().addIdsEqualToQuery(maxERevQbParameters, + alias1 + "." + originalIdPropertyName, alias2 +"." + originalIdPropertyName); + + // add subquery to rootParameters + String subqueryOperator = globalCfg.getCorrelatedSubqueryOperator(); + rootParameters.addWhere(revisionProperty, addAlias, subqueryOperator, maxERevQb); + } + + public void addAssociationAtRevisionRestriction(QueryBuilder rootQueryBuilder, String revisionProperty, + String revisionEndProperty, boolean addAlias, MiddleIdData referencingIdData, String versionsMiddleEntityName, + String eeOriginalIdPropertyPath, String revisionPropertyPath, + String originalIdPropertyName, String alias1, MiddleComponentData... componentDatas) { + Parameters rootParameters = rootQueryBuilder.getRootParameters(); + + // SELECT max(ee2.revision) FROM middleEntity ee2 + QueryBuilder maxEeRevQb = rootQueryBuilder.newSubQueryBuilder(versionsMiddleEntityName, MIDDLE_ENTITY_ALIAS_DEF_AUD_STR); + maxEeRevQb.addProjection("max", revisionPropertyPath, false); + // WHERE + Parameters maxEeRevQbParameters = maxEeRevQb.getRootParameters(); + // ee2.revision <= :revision + maxEeRevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", REVISION_PARAMETER); + // ee2.originalId.* = ee.originalId.* + String ee2OriginalIdPropertyPath = MIDDLE_ENTITY_ALIAS_DEF_AUD_STR + "." + originalIdPropertyName; + referencingIdData.getPrefixedMapper().addIdsEqualToQuery(maxEeRevQbParameters, eeOriginalIdPropertyPath, ee2OriginalIdPropertyPath); + for (MiddleComponentData componentData : componentDatas) { + componentData.getComponentMapper().addMiddleEqualToQuery(maxEeRevQbParameters, eeOriginalIdPropertyPath, alias1, ee2OriginalIdPropertyPath, MIDDLE_ENTITY_ALIAS_DEF_AUD_STR); + } + + // add subquery to rootParameters + rootParameters.addWhere(revisionProperty, addAlias, "=", maxEeRevQb); + } + +} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/ValidityAuditStrategy.java b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/ValidityAuditStrategy.java index 028dd13c73..6e2c6cc808 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/ValidityAuditStrategy.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/ValidityAuditStrategy.java @@ -16,6 +16,7 @@ import org.hibernate.LockOptions; import org.hibernate.Session; import org.hibernate.dialect.Dialect; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.envers.RevisionType; import org.hibernate.envers.configuration.AuditConfiguration; @@ -37,6 +38,8 @@ import org.hibernate.persister.entity.UnionSubclassEntityPersister; import org.hibernate.property.Getter; import org.hibernate.sql.Update; +import org.hibernate.type.CollectionType; +import org.hibernate.type.ComponentType; import org.hibernate.type.Type; import org.jboss.logging.Logger; @@ -234,21 +237,38 @@ private void autoFlushIfRequired( } @SuppressWarnings({"unchecked"}) - public void performCollectionChange(Session session, AuditConfiguration auditCfg, + public void performCollectionChange(Session session, String entityName, String propertyName, AuditConfiguration auditCfg, PersistentCollectionChangeData persistentCollectionChangeData, Object revision) { final QueryBuilder qb = new QueryBuilder(persistentCollectionChangeData.getEntityName(), MIDDLE_ENTITY_ALIAS); - // Adding a parameter for each id component, except the rev number final String originalIdPropName = auditCfg.getAuditEntCfg().getOriginalIdPropName(); - final Map originalId = (Map) persistentCollectionChangeData.getData().get( - originalIdPropName); + final Map originalId = (Map) persistentCollectionChangeData.getData().get(originalIdPropName); + final String revisionFieldName = auditCfg.getAuditEntCfg().getRevisionFieldName(); + final String revisionTypePropName = auditCfg.getAuditEntCfg().getRevisionTypePropName(); + + // Adding a parameter for each id component, except the rev number and type. for (Map.Entry originalIdEntry : originalId.entrySet()) { - if (!auditCfg.getAuditEntCfg().getRevisionFieldName().equals(originalIdEntry.getKey())) { + if (!revisionFieldName.equals(originalIdEntry.getKey()) && !revisionTypePropName.equals(originalIdEntry.getKey())) { qb.getRootParameters().addWhereWithParam(originalIdPropName + "." + originalIdEntry.getKey(), true, "=", originalIdEntry.getValue()); } } + final SessionFactoryImplementor sessionFactory = ( (SessionImplementor) session ).getFactory(); + final Type propertyType = sessionFactory.getEntityPersister( entityName ).getPropertyType( propertyName ); + if ( propertyType.isCollectionType() ) { + CollectionType collectionPropertyType = (CollectionType) propertyType; + // Handling collection of components. + if ( collectionPropertyType.getElementType( sessionFactory ) instanceof ComponentType ) { + // Adding restrictions to compare data outside of primary key. + for ( Map.Entry dataEntry : persistentCollectionChangeData.getData().entrySet() ) { + if ( !originalIdPropName.equals( dataEntry.getKey() ) ) { + qb.getRootParameters().addWhereWithParam( dataEntry.getKey(), true, "=", dataEntry.getValue() ); + } + } + } + } + addEndRevisionNullRestriction(auditCfg, qb.getRootParameters()); final List l = qb.toQuery(session).setLockOptions(LockOptions.UPGRADE).list(); @@ -280,7 +300,7 @@ public void addEntityAtRevisionRestriction(GlobalConfiguration globalCfg, QueryB public void addAssociationAtRevisionRestriction(QueryBuilder rootQueryBuilder, String revisionProperty, String revisionEndProperty, boolean addAlias, MiddleIdData referencingIdData, String versionsMiddleEntityName, String eeOriginalIdPropertyPath, String revisionPropertyPath, - String originalIdPropertyName, MiddleComponentData... componentDatas) { + String originalIdPropertyName, String alias1, MiddleComponentData... componentDatas) { Parameters rootParameters = rootQueryBuilder.getRootParameters(); addRevisionRestriction(rootParameters, revisionProperty, revisionEndProperty, addAlias); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java b/hibernate-envers/src/main/java/org/hibernate/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java index d3103eb319..4728690502 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java @@ -53,7 +53,7 @@ public PersistentCollectionChangeWorkUnit(SessionImplementor sessionImplementor, this.referencingPropertyName = referencingPropertyName; collectionChanges = auditCfg.getEntCfg().get(getEntityName()).getPropertyMapper() - .mapCollectionChanges(referencingPropertyName, collection, snapshot, id); + .mapCollectionChanges(sessionImplementor, referencingPropertyName, collection, snapshot, id); } public PersistentCollectionChangeWorkUnit(SessionImplementor sessionImplementor, String entityName, @@ -83,7 +83,7 @@ public void perform(Session session, Object revisionData) { ((Map) persistentCollectionChangeData.getData().get(entitiesCfg.getOriginalIdPropName())) .put(entitiesCfg.getRevisionFieldName(), revisionData); - auditStrategy.performCollectionChange(session, verCfg, persistentCollectionChangeData, revisionData); + auditStrategy.performCollectionChange(session, getEntityName(), referencingPropertyName, verCfg, persistentCollectionChangeData, revisionData); } } diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/StrTestNoProxyEntity.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/StrTestNoProxyEntity.java new file mode 100644 index 0000000000..7a1ef0fb09 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/StrTestNoProxyEntity.java @@ -0,0 +1,98 @@ +/* + * 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; + +import java.io.Serializable; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.annotations.Proxy; +import org.hibernate.envers.Audited; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +@Table(name = "STR_TEST_NP") +@Proxy(lazy = false) +public class StrTestNoProxyEntity implements Serializable { + @Id + @GeneratedValue + private Integer id; + + @Audited + private String str; + + public StrTestNoProxyEntity() { + } + + public StrTestNoProxyEntity(String str, Integer id) { + this.str = str; + this.id = id; + } + + public StrTestNoProxyEntity(String str) { + this.str = str; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getStr() { + return str; + } + + public void setStr(String str) { + this.str = str; + } + + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof StrTestNoProxyEntity ) ) return false; + + StrTestNoProxyEntity that = (StrTestNoProxyEntity) o; + + if ( id != null ? !id.equals( that.id ) : that.id != null ) return false; + if ( str != null ? !str.equals( that.str ) : that.str != null ) return false; + + return true; + } + + public int hashCode() { + int result = ( id != null ? id.hashCode() : 0 ); + result = 31 * result + ( str != null ? str.hashCode() : 0 ); + return result; + } + + public String toString() { + return "STNPE(id = " + id + ", str = " + str + ")"; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/collection/EmbeddableListEntity1.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/collection/EmbeddableListEntity1.java new file mode 100644 index 0000000000..8b93599693 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/collection/EmbeddableListEntity1.java @@ -0,0 +1,102 @@ +/* + * 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.collection; + +import java.util.ArrayList; +import java.util.List; +import javax.persistence.CollectionTable; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OrderColumn; +import javax.persistence.Table; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.test.entities.components.Component3; + +/** + * @author Kristoffer Lundberg (kristoffer at cambio dot se) + */ +@Entity +@Table(name = "EmbListEnt1") +@Audited +public class EmbeddableListEntity1 { + @Id + @GeneratedValue + private Integer id; + + private String otherData; + + @ElementCollection + @OrderColumn + @CollectionTable(name = "EmbListEnt1_list") + private List componentList = new ArrayList(); + + public EmbeddableListEntity1() { + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public List getComponentList() { + return componentList; + } + + public void setComponentList(List componentList) { + this.componentList = componentList; + } + + public String getOtherData() { + return otherData; + } + + public void setOtherData(String otherData) { + this.otherData = otherData; + } + + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof EmbeddableListEntity1 ) ) return false; + + EmbeddableListEntity1 that = (EmbeddableListEntity1) o; + + if ( id != null ? !id.equals( that.id ) : that.id != null ) return false; + + return true; + } + + public int hashCode() { + return ( id != null ? id.hashCode() : 0 ); + } + + public String toString() { + return "ELE1(id = " + id + ", otherData = " + otherData + ", componentList = " + componentList + ")"; + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/collection/EmbeddableListEntity2.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/collection/EmbeddableListEntity2.java new file mode 100644 index 0000000000..82029ab6fa --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/collection/EmbeddableListEntity2.java @@ -0,0 +1,94 @@ +/* + * 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.collection; + +import java.util.ArrayList; +import java.util.List; +import javax.persistence.CollectionTable; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OrderColumn; +import javax.persistence.Table; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.test.entities.components.relations.ManyToOneEagerComponent; + +/** + * Embeddable list with components encapsulating many-to-one relation (referencing some entity). + * + * @author thiagolrc + */ +@Entity +@Table(name = "EmbListEnt2") +@Audited +public class EmbeddableListEntity2 { + @Id + @GeneratedValue + private Integer id; + + @ElementCollection + @OrderColumn + @CollectionTable(name = "EmbListEnt2_list") + private List componentList = new ArrayList(); + + public EmbeddableListEntity2() { + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public List getComponentList() { + return componentList; + } + + public void setComponentList(List componentList) { + this.componentList = componentList; + } + + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof EmbeddableListEntity2 ) ) return false; + + EmbeddableListEntity2 that = (EmbeddableListEntity2) o; + + if ( id != null ? !id.equals( that.id ) : that.id != null ) return false; + + return true; + } + + public int hashCode() { + return ( id != null ? id.hashCode() : 0 ); + } + + public String toString() { + return "ELE2(id = " + id + ", componentList = " + componentList + ")"; + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/collection/EmbeddableMapEntity.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/collection/EmbeddableMapEntity.java new file mode 100644 index 0000000000..4144cbfa9f --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/collection/EmbeddableMapEntity.java @@ -0,0 +1,91 @@ +/* + * 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.collection; + +import java.util.HashMap; +import java.util.Map; +import javax.persistence.CollectionTable; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.test.entities.components.Component3; + +/** + * @author Kristoffer Lundberg (kristoffer at cambio dot se) + */ +@Entity +@Table(name = "EmbMapEnt") +public class EmbeddableMapEntity { + @Id + @GeneratedValue + private Integer id; + + @Audited + @ElementCollection + @CollectionTable(name = "EmbMapEnt_map") + private Map componentMap; + + public EmbeddableMapEntity() { + componentMap = new HashMap(); + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Map getComponentMap() { + return componentMap; + } + + public void setComponentMap(Map strings) { + this.componentMap = strings; + } + + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof EmbeddableMapEntity ) ) return false; + + EmbeddableMapEntity that = (EmbeddableMapEntity) o; + + if ( id != null ? !id.equals( that.id ) : that.id != null ) return false; + + return true; + } + + public int hashCode() { + return ( id != null ? id.hashCode() : 0 ); + } + + public String toString() { + return "EME(id = " + id + ", componentMap = " + componentMap + ")"; + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/collection/EmbeddableSetEntity.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/collection/EmbeddableSetEntity.java new file mode 100644 index 0000000000..e5a09afadf --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/collection/EmbeddableSetEntity.java @@ -0,0 +1,90 @@ +/* + * 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.collection; + +import java.util.HashSet; +import java.util.Set; +import javax.persistence.CollectionTable; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.test.entities.components.Component3; + +/** + * @author Kristoffer Lundberg (kristoffer at cambio dot se) + */ +@Entity +@Table(name = "EmbSetEnt") +@Audited +public class EmbeddableSetEntity { + @Id + @GeneratedValue + private Integer id; + + @ElementCollection + @CollectionTable(name = "EmbSetEnt_set") + private Set componentSet = new HashSet(); + + public EmbeddableSetEntity() { + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Set getComponentSet() { + return componentSet; + } + + public void setComponentSet(Set componentSet) { + this.componentSet = componentSet; + } + + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof EmbeddableSetEntity ) ) return false; + + EmbeddableSetEntity that = (EmbeddableSetEntity) o; + + if ( id != null ? !id.equals( that.id ) : that.id != null ) return false; + + return true; + } + + public int hashCode() { + return ( id != null ? id.hashCode() : 0 ); + } + + public String toString() { + return "ESE(id = " + id + ", componentSet = " + componentSet + ')'; + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/components/Component3.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/components/Component3.java new file mode 100644 index 0000000000..ac6f956b8c --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/components/Component3.java @@ -0,0 +1,121 @@ +/* + * 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.components; + +import javax.persistence.AttributeOverride; +import javax.persistence.AttributeOverrides; +import javax.persistence.Column; +import javax.persistence.Embeddable; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; + +/** + * The {@link #nonAuditedComponent} is ignored in {@link #hashCode()} + * and {@link #equals(Object)} since it's not audited. + * + * @author Kristoffer Lundberg (kristoffer at cambio dot se) + */ +@Embeddable +@Audited +public class Component3 { + private String str1; + + @AttributeOverrides({ + @AttributeOverride(name = "key", column = @Column(name = "audComp_key")), + @AttributeOverride(name = "value", column = @Column(name = "audComp_value")), + @AttributeOverride(name = "description", column = @Column(name = "audComp_description")) + }) + private Component4 auditedComponent; + + @NotAudited + @AttributeOverrides({ + @AttributeOverride(name = "key", column = @Column(name = "notAudComp_key")), + @AttributeOverride(name = "value", column = @Column(name = "notAudComp_value")), + @AttributeOverride(name = "description", column = @Column(name = "notAudComp_description")) + }) + private Component4 nonAuditedComponent; + + public Component3() { + } + + public Component3(String str1, Component4 auditedComponent, Component4 nonAuditedComponent) { + this.str1 = str1; + this.auditedComponent = auditedComponent; + this.nonAuditedComponent = nonAuditedComponent; + } + + public String getStr1() { + return str1; + } + + public void setStr1(String str1) { + this.str1 = str1; + } + + public Component4 getAuditedComponent() { + return auditedComponent; + } + + public void setAuditedComponent(Component4 auditedComponent) { + this.auditedComponent = auditedComponent; + } + + public Component4 getNonAuditedComponent() { + return nonAuditedComponent; + } + + public void setNonAuditedComponent(Component4 nonAuditedComponent) { + this.nonAuditedComponent = nonAuditedComponent; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ( ( auditedComponent == null ) ? 0 : auditedComponent.hashCode() ); + result = prime * result + ( ( str1 == null ) ? 0 : str1.hashCode() ); + return result; + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) return true; + if ( !( obj instanceof Component3 ) ) return false; + + Component3 other = (Component3) obj; + + if ( auditedComponent != null ? !auditedComponent.equals( other.auditedComponent ) : other.auditedComponent != null ) return false; + if ( str1 != null ? !str1.equals( other.str1 ) : other.str1 != null ) return false; + + return true; + } + + @Override + public String toString() { + return "Component3[str1 = " + str1 + ", auditedComponent = " + + auditedComponent + ", nonAuditedComponent = " + + nonAuditedComponent + "]"; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/components/Component4.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/components/Component4.java new file mode 100644 index 0000000000..20924c577d --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/components/Component4.java @@ -0,0 +1,104 @@ +/* + * 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.components; + +import javax.persistence.Embeddable; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; + +/** + * @author Kristoffer Lundberg (kristoffer at cambio dot se) + */ +@Embeddable +@Audited +public class Component4 { + private String key; + private String value; + + @NotAudited + private String description; + + public Component4() { + } + + public Component4(String key, String value, String description) { + this.key = key; + this.value = value; + this.description = description; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ( ( description == null ) ? 0 : description.hashCode() ); + result = prime * result + ( ( key == null ) ? 0 : key.hashCode() ); + result = prime * result + ( ( value == null ) ? 0 : value.hashCode() ); + return result; + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) return true; + if ( !( obj instanceof Component4 ) ) return false; + + Component4 other = (Component4) obj; + + if ( description != null ? !description.equals( other.description ) : other.description != null ) return false; + if ( key != null ? !key.equals( other.key ) : other.key != null ) return false; + if ( value != null ? !value.equals( other.value ) : other.value != null ) return false; + + return true; + } + + @Override + public String toString() { + return "Component4[key = " + key + ", value = " + value + ", description = " + description + "]"; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/components/relations/ManyToOneComponent.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/components/relations/ManyToOneComponent.java index dd8c92593a..2dd415791f 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/components/relations/ManyToOneComponent.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/components/relations/ManyToOneComponent.java @@ -22,6 +22,7 @@ * Boston, MA 02110-1301 USA */ package org.hibernate.envers.test.entities.components.relations; + import javax.persistence.Embeddable; import javax.persistence.ManyToOne; import javax.persistence.Table; diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/components/relations/ManyToOneEagerComponent.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/components/relations/ManyToOneEagerComponent.java new file mode 100644 index 0000000000..9de66b66ca --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/components/relations/ManyToOneEagerComponent.java @@ -0,0 +1,94 @@ +/* + * 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.components.relations; + +import javax.persistence.Embeddable; +import javax.persistence.FetchType; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.test.entities.StrTestNoProxyEntity; + +/** + * Do not mark as {@link Audited}. Should be implicitly treated as audited when part of audited entity. + * + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Embeddable +@Table(name = "ManyToOneEagerComp") +public class ManyToOneEagerComponent { + @ManyToOne(fetch = FetchType.EAGER) + private StrTestNoProxyEntity entity; + + private String data; + + public ManyToOneEagerComponent(StrTestNoProxyEntity entity, String data) { + this.entity = entity; + this.data = data; + } + + public ManyToOneEagerComponent() { + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public StrTestNoProxyEntity getEntity() { + return entity; + } + + public void setEntity(StrTestNoProxyEntity entity) { + this.entity = entity; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof ManyToOneEagerComponent ) ) return false; + + ManyToOneEagerComponent that = (ManyToOneEagerComponent) o; + + if ( data != null ? !data.equals( that.data ) : that.data != null ) return false; + if ( entity != null ? !entity.equals( that.entity ) : that.entity != null ) return false; + + return true; + } + + @Override + public int hashCode() { + int result = entity != null ? entity.hashCode() : 0; + result = 31 * result + ( data != null ? data.hashCode() : 0 ); + return result; + } + + public String toString() { + return "ManyToOneEagerComponent(data = " + data + ")"; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/BasicEmbeddableCollection.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/BasicEmbeddableCollection.java new file mode 100644 index 0000000000..6531197709 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/BasicEmbeddableCollection.java @@ -0,0 +1,130 @@ +/* + * 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.collection.embeddable; + +import java.util.Arrays; +import javax.persistence.EntityManager; + +import junit.framework.Assert; +import org.junit.Test; + +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.testing.TestForIssue; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@TestForIssue( jiraKey = "HHH-6613" ) +public class BasicEmbeddableCollection extends BaseEnversJPAFunctionalTestCase { + private int id = -1; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { DarkCharacter.class }; + } + + @Test + @Priority(10) + public void initData() { + EntityManager em = getEntityManager(); + + // Revision 1 - empty element collection + em.getTransaction().begin(); + DarkCharacter darkCharacter = new DarkCharacter( 1, 1 ); + em.persist( darkCharacter ); + em.getTransaction().commit(); + + id = darkCharacter.getId(); + + // Revision 2 - adding collection element + em.getTransaction().begin(); + darkCharacter = em.find( DarkCharacter.class, darkCharacter.getId() ); + darkCharacter.getNames().add( new Name( "Action", "Hank" ) ); + darkCharacter = em.merge( darkCharacter ); + em.getTransaction().commit(); + + // Revision 3 - adding another collection element + em.getTransaction().begin(); + darkCharacter = em.find( DarkCharacter.class, darkCharacter.getId() ); + darkCharacter.getNames().add( new Name( "Green", "Lantern" ) ); + darkCharacter = em.merge( darkCharacter ); + em.getTransaction().commit(); + + // Revision 4 - removing single collection element + em.getTransaction().begin(); + darkCharacter = em.find( DarkCharacter.class, darkCharacter.getId() ); + darkCharacter.getNames().remove( new Name( "Action", "Hank" ) ); + darkCharacter = em.merge( darkCharacter ); + em.getTransaction().commit(); + + // Revision 5 - removing all collection elements + em.getTransaction().begin(); + darkCharacter = em.find( DarkCharacter.class, darkCharacter.getId() ); + darkCharacter.getNames().clear(); + darkCharacter = em.merge( darkCharacter ); + em.getTransaction().commit(); + + em.close(); + } + + @Test + public void testRevisionsCount() { + Assert.assertEquals( Arrays.asList( 1, 2, 3, 4, 5 ), getAuditReader().getRevisions( DarkCharacter.class, id ) ); + } + + @Test + public void testHistoryOfCharacter() { + DarkCharacter darkCharacter = new DarkCharacter( id, 1 ); + + DarkCharacter ver1 = getAuditReader().find( DarkCharacter.class, id, 1 ); + + Assert.assertEquals( darkCharacter, ver1 ); + Assert.assertEquals( 0, ver1.getNames().size() ); + + darkCharacter.getNames().add( new Name( "Action", "Hank" ) ); + DarkCharacter ver2 = getAuditReader().find( DarkCharacter.class, id, 2 ); + + Assert.assertEquals( darkCharacter, ver2 ); + Assert.assertEquals( darkCharacter.getNames(), ver2.getNames() ); + + darkCharacter.getNames().add( new Name( "Green", "Lantern" ) ); + DarkCharacter ver3 = getAuditReader().find( DarkCharacter.class, id, 3 ); + + Assert.assertEquals( darkCharacter, ver3 ); + Assert.assertEquals( darkCharacter.getNames(), ver3.getNames() ); + + darkCharacter.getNames().remove( new Name( "Action", "Hank" ) ); + DarkCharacter ver4 = getAuditReader().find( DarkCharacter.class, id, 4 ); + + Assert.assertEquals( darkCharacter, ver4 ); + Assert.assertEquals( darkCharacter.getNames(), ver4.getNames() ); + + darkCharacter.getNames().clear(); + DarkCharacter ver5 = getAuditReader().find( DarkCharacter.class, id, 5 ); + + Assert.assertEquals( darkCharacter, ver5 ); + Assert.assertEquals( darkCharacter.getNames(), ver5.getNames() ); + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/DarkCharacter.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/DarkCharacter.java new file mode 100644 index 0000000000..79db3ff12d --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/DarkCharacter.java @@ -0,0 +1,105 @@ +/* + * 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.collection.embeddable; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.hibernate.envers.Audited; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +@Audited +public class DarkCharacter implements Serializable { + @Id + private int id; + + @ElementCollection + private Set names = new HashSet(); + + private int kills; + + public DarkCharacter() { + } + + public DarkCharacter(int id, int kills) { + this.id = id; + this.kills = kills; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( ! ( o instanceof DarkCharacter ) ) return false; + + DarkCharacter character = (DarkCharacter) o; + + if ( id != character.id ) return false; + if ( kills != character.kills ) return false; + + return true; + } + + @Override + public int hashCode() { + int result = id; + result = 31 * result + kills; + return result; + } + + @Override + public String toString() { + return "DarkCharacter(id = " + id + ", kills = " + kills + ")"; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getKills() { + return kills; + } + + public void setKills(int kills) { + this.kills = kills; + } + + public Set getNames() { + return names; + } + + public void setNames(Set names) { + this.names = names; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/EmbeddableList1.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/EmbeddableList1.java new file mode 100644 index 0000000000..49d207fae8 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/EmbeddableList1.java @@ -0,0 +1,117 @@ +/* + * 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.collection.embeddable; + +import java.util.Arrays; +import java.util.Collections; +import javax.persistence.EntityManager; + +import org.junit.Test; + +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.envers.test.entities.collection.EmbeddableListEntity1; +import org.hibernate.envers.test.entities.components.Component3; +import org.hibernate.envers.test.entities.components.Component4; +import org.hibernate.testing.TestForIssue; + +import static org.junit.Assert.assertEquals; + +/** + * @author Kristoffer Lundberg (kristoffer at cambio dot se) + */ +@TestForIssue( jiraKey = "HHH-6613" ) +public class EmbeddableList1 extends BaseEnversJPAFunctionalTestCase { + private Integer ele1_id = null; + + private final Component4 c4_1 = new Component4( "c41", "c41_value", "c41_description" ); + private final Component4 c4_2 = new Component4( "c42", "c42_value2", "c42_description" ); + private final Component3 c3_1 = new Component3( "c31", c4_1, c4_2 ); + private final Component3 c3_2 = new Component3( "c32", c4_1, c4_2 ); + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { EmbeddableListEntity1.class }; + } + + @Test + @Priority(10) + public void initData() { + EntityManager em = getEntityManager(); + + EmbeddableListEntity1 ele1 = new EmbeddableListEntity1(); + + // Revision 1 (ele1: initially 1 element in both collections) + em.getTransaction().begin(); + ele1.getComponentList().add( c3_1 ); + em.persist( ele1 ); + em.getTransaction().commit(); + + // Revision (still 1) (ele1: removing non-existing element) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity1.class, ele1.getId() ); + ele1.getComponentList().remove( c3_2 ); + em.getTransaction().commit(); + + // Revision 2 (ele1: adding one element) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity1.class, ele1.getId() ); + ele1.getComponentList().add( c3_2 ); + em.getTransaction().commit(); + + // Revision 3 (ele1: adding one existing element) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity1.class, ele1.getId() ); + ele1.getComponentList().add( c3_1 ); + em.getTransaction().commit(); + + // Revision 4 (ele1: removing one existing element) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity1.class, ele1.getId() ); + ele1.getComponentList().remove( c3_2 ); + em.getTransaction().commit(); + + ele1_id = ele1.getId(); + + em.close(); + } + + @Test + public void testRevisionsCounts() { + assertEquals( Arrays.asList( 1, 2, 3, 4 ), getAuditReader().getRevisions( EmbeddableListEntity1.class, ele1_id ) ); + } + + @Test + public void testHistoryOfEle1() { + EmbeddableListEntity1 rev1 = getAuditReader().find( EmbeddableListEntity1.class, ele1_id, 1 ); + EmbeddableListEntity1 rev2 = getAuditReader().find( EmbeddableListEntity1.class, ele1_id, 2 ); + EmbeddableListEntity1 rev3 = getAuditReader().find( EmbeddableListEntity1.class, ele1_id, 3 ); + EmbeddableListEntity1 rev4 = getAuditReader().find( EmbeddableListEntity1.class, ele1_id, 4 ); + + assertEquals( Collections.singletonList( c3_1 ), rev1.getComponentList() ); + assertEquals( Arrays.asList( c3_1, c3_2 ), rev2.getComponentList() ); + assertEquals( Arrays.asList( c3_1, c3_2, c3_1 ), rev3.getComponentList() ); + assertEquals( Arrays.asList( c3_1, c3_1 ), rev4.getComponentList() ); + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/EmbeddableList2.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/EmbeddableList2.java new file mode 100644 index 0000000000..4d325b8504 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/EmbeddableList2.java @@ -0,0 +1,233 @@ +/* + * 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.collection.embeddable; + +import java.util.Arrays; +import java.util.Date; +import javax.persistence.EntityManager; + +import org.junit.Test; + +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.envers.test.entities.StrTestNoProxyEntity; +import org.hibernate.envers.test.entities.collection.EmbeddableListEntity2; +import org.hibernate.envers.test.entities.components.relations.ManyToOneEagerComponent; +import org.hibernate.testing.TestForIssue; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * Checks if many-to-one relations inside an embedded component list are being audited. + * + * @author thiagolrc + */ +@TestForIssue( jiraKey = "HHH-6613" ) +public class EmbeddableList2 extends BaseEnversJPAFunctionalTestCase { + private Integer ele_id1 = null; + + private StrTestNoProxyEntity entity1 = new StrTestNoProxyEntity( "strTestEntity1" ); + private StrTestNoProxyEntity entity2 = new StrTestNoProxyEntity( "strTestEntity2" ); + private StrTestNoProxyEntity entity3 = new StrTestNoProxyEntity( "strTestEntity3" ); + private StrTestNoProxyEntity entity4 = new StrTestNoProxyEntity( "strTestEntity3" ); + private StrTestNoProxyEntity entity4Copy = null; + + private ManyToOneEagerComponent manyToOneComponent1 = new ManyToOneEagerComponent( entity1, "dataComponent1" ); + private ManyToOneEagerComponent manyToOneComponent2 = new ManyToOneEagerComponent( entity2, "dataComponent2" ); + private ManyToOneEagerComponent manyToOneComponent4 = new ManyToOneEagerComponent( entity4, "dataComponent4" ); + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { EmbeddableListEntity2.class, StrTestNoProxyEntity.class }; + } + + @Test + @Priority(10) + public void initData() { + EntityManager em = getEntityManager(); + + // Revision 1 (ele1: saving a list with 1 many-to-one component) + em.getTransaction().begin(); + EmbeddableListEntity2 ele1 = new EmbeddableListEntity2(); + em.persist( entity1 ); //persisting the entities referenced by the components + em.persist( entity2 ); + ele1.getComponentList().add( manyToOneComponent1 ); + em.persist( ele1 ); + em.getTransaction().commit(); + ele_id1 = ele1.getId(); + + // Revision 2 (ele1: changing the component) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity2.class, ele1.getId() ); + ele1.getComponentList().clear(); + ele1.getComponentList().add( manyToOneComponent2 ); + em.getTransaction().commit(); + + //Revision 3 (ele1: putting back the many-to-one component to the list) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity2.class, ele1.getId() ); + ele1.getComponentList().add( manyToOneComponent1 ); + em.getTransaction().commit(); + + // Revision 4 (ele1: changing the component's entity) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity2.class, ele1.getId() ); + em.persist( entity3 ); + ele1.getComponentList().get( ele1.getComponentList().indexOf( manyToOneComponent2 ) ).setEntity( entity3 ); + ele1.getComponentList().get( ele1.getComponentList().indexOf( manyToOneComponent2 ) ).setData( "dataComponent3" ); + em.getTransaction().commit(); + + // Revision 5 (ele1: adding a new many-to-one component) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity2.class, ele1.getId() ); + em.persist( entity4 ); + entity4Copy = new StrTestNoProxyEntity( entity4.getStr(), entity4.getId() ); + ele1.getComponentList().add( manyToOneComponent4 ); + em.getTransaction().commit(); + + // Revision 6 (ele1: changing the component's entity properties) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity2.class, ele1.getId() ); + ele1.getComponentList().get( ele1.getComponentList().indexOf( manyToOneComponent4 ) ).getEntity().setStr( "sat4" ); + em.getTransaction().commit(); + + // Revision 7 (ele1: removing component) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity2.class, ele1.getId() ); + ele1.getComponentList().remove( ele1.getComponentList().indexOf( manyToOneComponent4 ) ); + em.getTransaction().commit(); + + // Revision 8 (ele1: removing all) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity2.class, ele1.getId() ); + em.remove( ele1 ); + em.getTransaction().commit(); + + em.close(); + } + + @Test + public void testRevisionsCounts() { + assertEquals( + Arrays.asList( 1, 2, 3, 4, 5, 7, 8 ), + getAuditReader().getRevisions( EmbeddableListEntity2.class, ele_id1 ) + ); + assertEquals( + Arrays.asList( 1 ), getAuditReader().getRevisions( StrTestNoProxyEntity.class, entity1.getId() ) + ); + assertEquals( + Arrays.asList( 1 ), getAuditReader().getRevisions( StrTestNoProxyEntity.class, entity2.getId() ) + ); + assertEquals( + Arrays.asList( 4 ), getAuditReader().getRevisions( StrTestNoProxyEntity.class, entity3.getId() ) + ); + assertEquals( + Arrays.asList( 5, 6 ), + getAuditReader().getRevisions( StrTestNoProxyEntity.class, entity4.getId() ) + ); + } + + @Test + public void testManyToOneComponentList() { + // Revision 1: many-to-one component1 in the list + EmbeddableListEntity2 rev1 = getAuditReader().find( EmbeddableListEntity2.class, ele_id1, 1 ); + assertNotNull( "Revision not found", rev1 ); + assertTrue( "The component collection was not audited", rev1.getComponentList().size() > 0 ); + assertEquals( + "The component primitive property was not audited", + "dataComponent1", rev1.getComponentList().get( 0 ).getData() + ); + assertEquals( + "The component manyToOne reference was not audited", + entity1, rev1.getComponentList().get( 0 ).getEntity() + ); + } + + @Test + public void testHistoryOfEle1() { + // Revision 1: many-to-one component in the list + assertEquals( + Arrays.asList( new ManyToOneEagerComponent( entity1, "dataComponent1" ) ), + getAuditReader().find( EmbeddableListEntity2.class, ele_id1, 1 ).getComponentList() + ); + + // Revision 2: many-to-one component in the list + assertEquals( + Arrays.asList( new ManyToOneEagerComponent( entity2, "dataComponent2" ) ), + getAuditReader().find( EmbeddableListEntity2.class, ele_id1, 2 ).getComponentList() + ); + + // Revision 3: two many-to-one components in the list + assertEquals( + Arrays.asList( + new ManyToOneEagerComponent( entity2, "dataComponent2" ), + new ManyToOneEagerComponent( entity1, "dataComponent1" ) + ), + getAuditReader().find( EmbeddableListEntity2.class, ele_id1, 3 ).getComponentList() + ); + + // Revision 4: second component edited and first one in the list + assertEquals( + Arrays.asList( + new ManyToOneEagerComponent( entity3, "dataComponent3" ), + new ManyToOneEagerComponent( entity1, "dataComponent1" ) + ), + getAuditReader().find( EmbeddableListEntity2.class, ele_id1, 4 ).getComponentList() + ); + + // Revision 5: fourth component added in the list + assertEquals( + Arrays.asList( + new ManyToOneEagerComponent( entity3, "dataComponent3" ), + new ManyToOneEagerComponent( entity1, "dataComponent1" ), + new ManyToOneEagerComponent( entity4Copy, "dataComponent4" ) + ), + getAuditReader().find( EmbeddableListEntity2.class, ele_id1, 5 ).getComponentList() + ); + + // Revision 6: changing fourth component property + assertEquals( + Arrays.asList( + new ManyToOneEagerComponent( entity3, "dataComponent3" ), + new ManyToOneEagerComponent( entity1, "dataComponent1" ), + new ManyToOneEagerComponent( entity4, "dataComponent4" ) + ), + getAuditReader().find( EmbeddableListEntity2.class, ele_id1, 6 ).getComponentList() + ); + + // Revision 7: removing component number four + assertEquals( + Arrays.asList( + new ManyToOneEagerComponent( entity3, "dataComponent3" ), + new ManyToOneEagerComponent( entity1, "dataComponent1" ) + ), + getAuditReader().find( EmbeddableListEntity2.class, ele_id1, 7 ).getComponentList() + ); + + assertNull( getAuditReader().find( EmbeddableListEntity2.class, ele_id1, 8 ) ); + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/EmbeddableMap.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/EmbeddableMap.java new file mode 100644 index 0000000000..2a2895dd77 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/EmbeddableMap.java @@ -0,0 +1,136 @@ +/* + * 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.collection.embeddable; + +import java.util.Arrays; +import java.util.Collections; + +import javax.persistence.EntityManager; + +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.envers.test.entities.collection.EmbeddableMapEntity; +import org.hibernate.envers.test.entities.components.Component3; +import org.hibernate.envers.test.entities.components.Component4; +import org.hibernate.envers.test.tools.TestTools; +import org.hibernate.testing.TestForIssue; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author Kristoffer Lundberg (kristoffer at cambio dot se) + */ +@TestForIssue( jiraKey = "HHH-6613" ) +public class EmbeddableMap extends BaseEnversJPAFunctionalTestCase { + private Integer eme1_id = null; + private Integer eme2_id = null; + + private final Component4 c4_1 = new Component4( "c41", "c41_value", "c41_description" ); + private final Component4 c4_2 = new Component4( "c42", "c42_value2", "c42_description" ); + private final Component3 c3_1 = new Component3( "c31", c4_1, c4_2 ); + private final Component3 c3_2 = new Component3( "c32", c4_1, c4_2 ); + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { EmbeddableMapEntity.class }; + } + + @Test + @Priority(10) + public void initData() { + EntityManager em = getEntityManager(); + + EmbeddableMapEntity eme1 = new EmbeddableMapEntity(); + EmbeddableMapEntity eme2 = new EmbeddableMapEntity(); + + // Revision 1 (eme1: initialy empty, eme2: initialy 1 mapping) + em.getTransaction().begin(); + eme2.getComponentMap().put( "1", c3_1 ); + em.persist( eme1 ); + em.persist( eme2 ); + em.getTransaction().commit(); + + // Revision 2 (eme1: adding 2 mappings, eme2: no changes) + em.getTransaction().begin(); + eme1 = em.find( EmbeddableMapEntity.class, eme1.getId() ); + eme2 = em.find( EmbeddableMapEntity.class, eme2.getId() ); + eme1.getComponentMap().put( "1", c3_1 ); + eme1.getComponentMap().put( "2", c3_2 ); + em.getTransaction().commit(); + + // Revision 3 (eme1: removing an existing mapping, eme2: replacing a value) + em.getTransaction().begin(); + eme1 = em.find( EmbeddableMapEntity.class, eme1.getId() ); + eme2 = em.find( EmbeddableMapEntity.class, eme2.getId() ); + eme1.getComponentMap().remove( "1" ); + eme2.getComponentMap().put( "1", c3_2 ); + em.getTransaction().commit(); + + // No revision (eme1: removing a non-existing mapping, eme2: replacing with the same value) + em.getTransaction().begin(); + eme1 = em.find( EmbeddableMapEntity.class, eme1.getId() ); + eme2 = em.find( EmbeddableMapEntity.class, eme2.getId() ); + eme1.getComponentMap().remove( "3" ); + eme2.getComponentMap().put( "1", c3_2 ); + em.getTransaction().commit(); + + eme1_id = eme1.getId(); + eme2_id = eme2.getId(); + + em.close(); + } + + @Test + public void testRevisionsCounts() { + Assert.assertEquals( Arrays.asList( 1, 2, 3 ), getAuditReader().getRevisions( EmbeddableMapEntity.class, eme1_id ) ); + Assert.assertEquals( Arrays.asList( 1, 3 ), getAuditReader().getRevisions( EmbeddableMapEntity.class, eme2_id ) ); + } + + @Test + public void testHistoryOfEme1() { + EmbeddableMapEntity rev1 = getAuditReader().find( EmbeddableMapEntity.class, eme1_id, 1 ); + EmbeddableMapEntity rev2 = getAuditReader().find( EmbeddableMapEntity.class, eme1_id, 2 ); + EmbeddableMapEntity rev3 = getAuditReader().find( EmbeddableMapEntity.class, eme1_id, 3 ); + EmbeddableMapEntity rev4 = getAuditReader().find( EmbeddableMapEntity.class, eme1_id, 4 ); + + Assert.assertEquals( Collections.EMPTY_MAP, rev1.getComponentMap() ); + Assert.assertEquals( TestTools.makeMap( "1", c3_1, "2", c3_2 ), rev2.getComponentMap() ); + Assert.assertEquals( TestTools.makeMap( "2", c3_2 ), rev3.getComponentMap() ); + Assert.assertEquals( TestTools.makeMap( "2", c3_2 ), rev4.getComponentMap() ); + } + + @Test + public void testHistoryOfEme2() { + EmbeddableMapEntity rev1 = getAuditReader().find( EmbeddableMapEntity.class, eme2_id, 1 ); + EmbeddableMapEntity rev2 = getAuditReader().find( EmbeddableMapEntity.class, eme2_id, 2 ); + EmbeddableMapEntity rev3 = getAuditReader().find( EmbeddableMapEntity.class, eme2_id, 3 ); + EmbeddableMapEntity rev4 = getAuditReader().find( EmbeddableMapEntity.class, eme2_id, 4 ); + + Assert.assertEquals( TestTools.makeMap( "1", c3_1 ), rev1.getComponentMap() ); + Assert.assertEquals( TestTools.makeMap( "1", c3_1 ), rev2.getComponentMap() ); + Assert.assertEquals( TestTools.makeMap( "1", c3_2 ), rev3.getComponentMap() ); + Assert.assertEquals( TestTools.makeMap( "1", c3_2 ), rev4.getComponentMap() ); + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/EmbeddableSet.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/EmbeddableSet.java new file mode 100644 index 0000000000..32c93332db --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/EmbeddableSet.java @@ -0,0 +1,116 @@ +/* + * 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.collection.embeddable; + +import java.util.Arrays; +import java.util.Collections; +import javax.persistence.EntityManager; + +import org.junit.Test; + +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.envers.test.entities.collection.EmbeddableSetEntity; +import org.hibernate.envers.test.entities.components.Component3; +import org.hibernate.envers.test.entities.components.Component4; +import org.hibernate.envers.test.tools.TestTools; +import org.hibernate.testing.TestForIssue; + +import static org.junit.Assert.assertEquals; + +/** + * @author Kristoffer Lundberg (kristoffer at cambio dot se) + */ +@TestForIssue( jiraKey = "HHH-6613" ) +public class EmbeddableSet extends BaseEnversJPAFunctionalTestCase { + private Integer ese1_id = null; + + private final Component4 c4_1 = new Component4( "c41", "c41_value", "c41_description" ); + private final Component4 c4_2 = new Component4( "c42", "c42_value2", "c42_description" ); + private final Component3 c3_1 = new Component3( "c31", c4_1, c4_2 ); + private final Component3 c3_2 = new Component3( "c32", c4_1, c4_2 ); + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { EmbeddableSetEntity.class }; + } + + @Test + @Priority(10) + public void initData() { + EntityManager em = getEntityManager(); + + EmbeddableSetEntity ese1 = new EmbeddableSetEntity(); + + // Revision 1 (ese1: initially 1 element in both collections) + em.getTransaction().begin(); + ese1.getComponentSet().add( c3_1 ); + em.persist( ese1 ); + em.getTransaction().commit(); + + // Revision (still 1) (ese1: removing non-existing element) + em.getTransaction().begin(); + ese1 = em.find( EmbeddableSetEntity.class, ese1.getId() ); + ese1.getComponentSet().remove( c3_2 ); + em.getTransaction().commit(); + + // Revision 2 (ese1: adding one element) + em.getTransaction().begin(); + ese1 = em.find( EmbeddableSetEntity.class, ese1.getId() ); + ese1.getComponentSet().add( c3_2 ); + em.getTransaction().commit(); + + // Revision 3 (ese1: adding one existing element) + em.getTransaction().begin(); + ese1 = em.find( EmbeddableSetEntity.class, ese1.getId() ); + ese1.getComponentSet().add( c3_1 ); + em.getTransaction().commit(); + + // Revision 4 (ese1: removing one existing element) + em.getTransaction().begin(); + ese1 = em.find( EmbeddableSetEntity.class, ese1.getId() ); + ese1.getComponentSet().remove( c3_2 ); + em.getTransaction().commit(); + + ese1_id = ese1.getId(); + + em.close(); + } + + @Test + public void testRevisionsCounts() { + assertEquals( Arrays.asList( 1, 2, 3 ), getAuditReader().getRevisions( EmbeddableSetEntity.class, ese1_id ) ); + } + + @Test + public void testHistoryOfEse1() { + EmbeddableSetEntity rev1 = getAuditReader().find( EmbeddableSetEntity.class, ese1_id, 1 ); + EmbeddableSetEntity rev2 = getAuditReader().find( EmbeddableSetEntity.class, ese1_id, 2 ); + EmbeddableSetEntity rev3 = getAuditReader().find( EmbeddableSetEntity.class, ese1_id, 3 ); + + assertEquals( Collections.singleton( c3_1 ), rev1.getComponentSet() ); + assertEquals( TestTools.makeSet( c3_1, c3_2 ), rev2.getComponentSet() ); + assertEquals( TestTools.makeSet( c3_1 ), rev3.getComponentSet() ); + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/Name.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/Name.java new file mode 100644 index 0000000000..c9581814ff --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/embeddable/Name.java @@ -0,0 +1,87 @@ +/* + * 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.collection.embeddable; + +import java.io.Serializable; +import javax.persistence.Embeddable; + +import org.hibernate.envers.Audited; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Embeddable +@Audited +public class Name implements Serializable { + private String firstName; + private String lastName; + + public Name() { + } + + public Name(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof Name ) ) return false; + + Name name = (Name) o; + if ( firstName != null ? !firstName.equals( name.firstName ) : name.firstName != null ) return false; + if ( lastName != null ? !lastName.equals( name.lastName ) : name.lastName != null ) return false; + + return true; + } + + @Override + public int hashCode() { + int result = firstName != null ? firstName.hashCode() : 0; + result = 31 * result + ( lastName != null ? lastName.hashCode() : 0 ); + return result; + } + + @Override + public String toString() { + return "Name(firstName = " + firstName + ", lastName = " + lastName + ")"; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/modifiedflags/HasChangedComponentCollection.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/modifiedflags/HasChangedComponentCollection.java new file mode 100644 index 0000000000..cfb4953b00 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/modifiedflags/HasChangedComponentCollection.java @@ -0,0 +1,102 @@ +package org.hibernate.envers.test.integration.modifiedflags; + +import java.util.List; +import javax.persistence.EntityManager; + +import org.junit.Test; + +import org.hibernate.envers.test.Priority; +import org.hibernate.envers.test.entities.collection.EmbeddableListEntity1; +import org.hibernate.envers.test.entities.components.Component3; +import org.hibernate.envers.test.entities.components.Component4; +import org.hibernate.testing.TestForIssue; + +import static org.junit.Assert.assertEquals; +import static org.hibernate.envers.test.tools.TestTools.extractRevisionNumbers; +import static org.hibernate.envers.test.tools.TestTools.makeList; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@TestForIssue( jiraKey = "HHH-6613" ) +public class HasChangedComponentCollection extends AbstractModifiedFlagsEntityTest { + private Integer ele1_id = null; + + private final Component4 c4_1 = new Component4( "c41", "c41_value", "c41_description" ); + private final Component4 c4_2 = new Component4( "c42", "c42_value2", "c42_description" ); + private final Component3 c3_1 = new Component3( "c31", c4_1, c4_2 ); + private final Component3 c3_2 = new Component3( "c32", c4_1, c4_2 ); + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { EmbeddableListEntity1.class }; + } + + @Test + @Priority(10) + public void initData() { + EntityManager em = getEntityManager(); + + // Revision 1 (ele1: initially 1 element in both collections) + em.getTransaction().begin(); + EmbeddableListEntity1 ele1 = new EmbeddableListEntity1(); + ele1.setOtherData( "data" ); + ele1.getComponentList().add( c3_1 ); + em.persist( ele1 ); + em.getTransaction().commit(); + + // Revision (still 1) (ele1: removing non-existing element) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity1.class, ele1.getId() ); + ele1.getComponentList().remove( c3_2 ); + em.getTransaction().commit(); + + // Revision 2 (ele1: updating singular property and removing non-existing element) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity1.class, ele1.getId() ); + ele1.setOtherData( "modified" ); + ele1.getComponentList().remove( c3_2 ); + ele1 = em.merge( ele1 ); + em.getTransaction().commit(); + + // Revision 3 (ele1: adding one element) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity1.class, ele1.getId() ); + ele1.getComponentList().add( c3_2 ); + em.getTransaction().commit(); + + // Revision 4 (ele1: adding one existing element) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity1.class, ele1.getId() ); + ele1.getComponentList().add( c3_1 ); + em.getTransaction().commit(); + + // Revision 5 (ele1: removing one existing element) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity1.class, ele1.getId() ); + ele1.getComponentList().remove( c3_2 ); + em.getTransaction().commit(); + + // Revision 6 (ele1: changing singular property only) + em.getTransaction().begin(); + ele1 = em.find( EmbeddableListEntity1.class, ele1.getId() ); + ele1.setOtherData( "another modification" ); + ele1 = em.merge( ele1 ); + em.getTransaction().commit(); + + ele1_id = ele1.getId(); + + em.close(); + } + + @Test + public void testHasChangedEle() { + List list = queryForPropertyHasChanged( EmbeddableListEntity1.class, ele1_id, "componentList" ); + assertEquals( 4, list.size() ); + assertEquals( makeList( 1, 3, 4, 5 ), extractRevisionNumbers( list ) ); + + list = queryForPropertyHasChanged( EmbeddableListEntity1.class, ele1_id, "otherData" ); + assertEquals( 3, list.size() ); + assertEquals( makeList( 1, 2, 6 ), extractRevisionNumbers( list ) ); + } +} \ No newline at end of file