HHH-6613 - Support for audited @ElementCollection of embeddables

This commit is contained in:
Lukasz Antoniak 2013-02-28 13:22:14 +01:00
parent fa804c1577
commit 797819ff07
57 changed files with 2494 additions and 378 deletions

View File

@ -1304,7 +1304,8 @@ query.add(AuditEntity.relatedId("address").eq(relatedEntityId));]]></programlist
<orderedlist>
<listitem>
<para>
collections of components
Bag style collection which identifier column has been defined using
<interfacename>@CollectionId</interfacename> annotation (JIRA ticket HHH-3950).
</para>
</listitem>
</orderedlist>

View File

@ -140,13 +140,13 @@ public final class AuditMetadataGenerator {
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 @@ public final class AuditMetadataGenerator {
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);
}

View File

@ -38,13 +38,18 @@ import org.dom4j.Element;
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.SortedMapCollectionMapper;
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.MappingTools;
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.Property;
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 @@ public final class CollectionMetadataGenerator {
// 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 @@ public final class CollectionMetadataGenerator {
// 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 @@ public final class CollectionMetadataGenerator {
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 @@ public final class CollectionMetadataGenerator {
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<Set>(commonCollectionMapperData,
HashSet.class, SetProxy.class, elementComponentData));
HashSet.class, SetProxy.class, elementComponentData, embeddableElementType));
} else if (type instanceof SortedMapType) {
// Indexed collection, so <code>indexComponentData</code> 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 <code>indexComponentData</code> is not null.
currentMapper.addComposite(propertyAuditingData.getPropertyData(),
new MapCollectionMapper<Map>(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<List>(commonCollectionMapperData,
ArrayList.class, ListProxy.class, elementComponentData));
ArrayList.class, ListProxy.class, elementComponentData, embeddableElementType));
} else if (type instanceof ListType) {
// Indexed collection, so <code>indexComponentData</code> 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 @@ public final class CollectionMetadataGenerator {
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) {

View File

@ -49,15 +49,17 @@ public final class QueryGeneratorBuilder {
private final MiddleIdData referencingIdData;
private final String auditMiddleEntityName;
private final List<MiddleIdData> 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<MiddleIdData>();
}
@ -69,14 +71,14 @@ public final class QueryGeneratorBuilder {
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 @@ public final class QueryGeneratorBuilder {
}
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.");
}

View File

@ -319,7 +319,7 @@ public class AuditedPropertiesReader {
// 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 @@ public class AuditedPropertiesReader {
allClassAudited);
PersistentPropertiesSource componentPropertiesSource = new ComponentPropertiesSource(
(Component) propertyValue);
reflectionManager, propertyValue
);
ComponentAuditedPropertiesReader audPropReader = new ComponentAuditedPropertiesReader(
ModificationStore.FULL, componentPropertiesSource,
@ -540,11 +541,11 @@ public class AuditedPropertiesReader {
public Class<? extends Annotation> 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) {

View File

@ -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 class ComponentAuditingData extends PropertyAuditingData implements Audit
public boolean contains(String propertyName) {
return properties.containsKey(propertyName);
}
}
public Set<String> getPropertyNames() {
return properties.keySet();
}
}

View File

@ -25,11 +25,9 @@ package org.hibernate.envers.entities;
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 class EntityInstantiator {
addTo.add(createInstanceFromVersionsEntity(entityName, versionsEntity, revision));
}
}
public AuditConfiguration getAuditConfiguration() {
return verCfg;
}
public AuditReaderImplementor getAuditReaderImplementor() {
return versionsReader;
}
}

View File

@ -130,11 +130,13 @@ public class ComponentPropertyMapper implements PropertyMapper, CompositeMapperB
}
}
public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
PersistentCollection newColl,
Serializable oldColl,
Serializable id) {
return delegate.mapCollectionChanges(referencingPropertyName, newColl, oldColl, id);
public List<PersistentCollectionChangeData> mapCollectionChanges(SessionImplementor session, String referencingPropertyName,
PersistentCollection newColl,
Serializable oldColl, Serializable id) {
return delegate.mapCollectionChanges(session, referencingPropertyName, newColl, oldColl, id);
}
public Map<PropertyData, PropertyMapper> getProperties() {
return delegate.getProperties();
}
}

View File

@ -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 @@ import org.hibernate.envers.entities.PropertyData;
public interface CompositeMapperBuilder extends SimpleMapperBuilder {
public CompositeMapperBuilder addComponent(PropertyData propertyData, String componentClassName);
public void addComposite(PropertyData propertyData, PropertyMapper propertyMapper);
public Map<PropertyData, PropertyMapper> getProperties();
}

View File

@ -171,14 +171,14 @@ public class MultiPropertyMapper implements ExtendedPropertyMapper {
}
}
public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
PersistentCollection newColl,
Serializable oldColl,
Serializable id) {
public List<PersistentCollectionChangeData> mapCollectionChanges(SessionImplementor session,
String referencingPropertyName,
PersistentCollection newColl,
Serializable oldColl, Serializable id) {
Pair<PropertyMapper, String> 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;
}

View File

@ -59,14 +59,15 @@ public interface PropertyMapper {
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<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
List<PersistentCollectionChangeData> mapCollectionChanges(SessionImplementor session, String referencingPropertyName,
PersistentCollection newColl,
Serializable oldColl, Serializable id);

View File

@ -115,10 +115,10 @@ public class SinglePropertyMapper implements PropertyMapper, SimpleMapperBuilder
}
}
public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
public List<PersistentCollectionChangeData> mapCollectionChanges(SessionImplementor sessionImplementor,
String referencingPropertyName,
PersistentCollection newColl,
Serializable oldColl,
Serializable id) {
Serializable oldColl, Serializable id) {
return null;
}

View File

@ -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 class SubclassPropertyMapper implements ExtendedPropertyMapper {
main.mapToEntityFromMap(verCfg, obj, data, primaryKey, versionsReader, revision);
}
public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
PersistentCollection newColl,
Serializable oldColl,
Serializable id) {
public List<PersistentCollectionChangeData> mapCollectionChanges(SessionImplementor session, String referencingPropertyName,
PersistentCollection newColl,
Serializable oldColl, Serializable id) {
List<PersistentCollectionChangeData> parentCollectionChanges = parentMapper.mapCollectionChanges(
referencingPropertyName, newColl, oldColl, id);
session, referencingPropertyName, newColl, oldColl, id);
List<PersistentCollectionChangeData> mainCollectionChanges = main.mapCollectionChanges(
referencingPropertyName, newColl, oldColl, id);
session, referencingPropertyName, newColl, oldColl, id);
if (parentCollectionChanges == null) {
return mainCollectionChanges;
@ -109,4 +109,11 @@ public class SubclassPropertyMapper implements ExtendedPropertyMapper {
public void add(PropertyData propertyData) {
main.add(propertyData);
}
public Map<PropertyData, PropertyMapper> getProperties() {
final Map<PropertyData, PropertyMapper> joinedProperties = new HashMap<PropertyData, PropertyMapper>();
joinedProperties.putAll(parentMapper.getProperties());
joinedProperties.putAll(main.getProperties());
return joinedProperties;
}
}

View File

@ -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 @@ import org.hibernate.property.Setter;
public abstract class AbstractCollectionMapper<T> implements PropertyMapper {
protected final CommonCollectionMapperData commonCollectionMapperData;
protected final Class<? extends T> collectionClass;
protected final boolean revisionTypeInId;
private final Constructor<? extends T> proxyConstructor;
protected AbstractCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
Class<? extends T> collectionClass, Class<? extends T> proxyClass) {
Class<? extends T> collectionClass, Class<? extends T> proxyClass,
boolean revisionTypeInId) {
this.commonCollectionMapperData = commonCollectionMapperData;
this.collectionClass = collectionClass;
this.revisionTypeInId = revisionTypeInId;
try {
proxyConstructor = proxyClass.getConstructor(Initializor.class);
@ -74,13 +78,14 @@ public abstract class AbstractCollectionMapper<T> implements PropertyMapper {
/**
* 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<String, Object> data, Object changed);
protected abstract void mapToMapFromObject(SessionImplementor session, Map<String, Object> idData, Map<String, Object> data, Object changed);
private void addCollectionChanges(List<PersistentCollectionChangeData> collectionChanges, Set<Object> changed,
RevisionType revisionType, Serializable id) {
private void addCollectionChanges(SessionImplementor session, List<PersistentCollectionChangeData> collectionChanges,
Set<Object> changed, RevisionType revisionType, Serializable id) {
for (Object changedObj : changed) {
Map<String, Object> entityData = new HashMap<String, Object>();
Map<String, Object> originalId = new HashMap<String, Object>();
@ -92,18 +97,18 @@ public abstract class AbstractCollectionMapper<T> implements PropertyMapper {
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<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
public List<PersistentCollectionChangeData> 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 abstract class AbstractCollectionMapper<T> implements PropertyMapper {
// 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<Object> deleted = new HashSet<Object>();
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 abstract class AbstractCollectionMapper<T> implements PropertyMapper {
} else if (isFromNullToEmptyOrFromEmptyToNull((PersistentCollection) newObj, (Serializable) oldObj)) {
data.put(propertyData.getModifiedFlagPropertyName(), true);
} else {
List<PersistentCollectionChangeData> changes = mapCollectionChanges(
List<PersistentCollectionChangeData> changes = mapCollectionChanges(session,
commonCollectionMapperData.getCollectionReferencingPropertyData().getName(),
(PersistentCollection) newObj, (Serializable) oldObj, null);
data.put(propertyData.getModifiedFlagPropertyName(), !changes.isEmpty());

View File

@ -42,9 +42,9 @@ public abstract class AbstractToOneMapper implements PropertyMapper {
}
@Override
public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
PersistentCollection newColl,
Serializable oldColl, Serializable id) {
public List<PersistentCollectionChangeData> mapCollectionChanges(SessionImplementor session, String referencingPropertyName,
PersistentCollection newColl, Serializable oldColl,
Serializable id) {
return null;
}

View File

@ -28,6 +28,7 @@ import java.util.Collection;
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<T extends Collection> extends AbstractCollect
public BasicCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
Class<? extends T> collectionClass, Class<? extends T> proxyClass,
MiddleComponentData elementComponentData) {
super(commonCollectionMapperData, collectionClass, proxyClass);
MiddleComponentData elementComponentData, boolean revisionTypeInId) {
super(commonCollectionMapperData, collectionClass, proxyClass, revisionTypeInId);
this.elementComponentData = elementComponentData;
}
@ -67,7 +68,7 @@ public class BasicCollectionMapper<T extends Collection> extends AbstractCollect
}
}
protected void mapToMapFromObject(Map<String, Object> data, Object changed) {
elementComponentData.getComponentMapper().mapToMapFromObject(data, changed);
protected void mapToMapFromObject(SessionImplementor session, Map<String, Object> idData, Map<String, Object> data, Object changed) {
elementComponentData.getComponentMapper().mapToMapFromObject(session, idData, data, changed);
}
}

View File

@ -28,6 +28,7 @@ import java.util.List;
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<List> 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 @@ public final class ListCollectionMapper extends AbstractCollectionMapper<List> i
}
@SuppressWarnings({"unchecked"})
protected void mapToMapFromObject(Map<String, Object> data, Object changed) {
protected void mapToMapFromObject(SessionImplementor session, Map<String, Object> idData, Map<String, Object> data, Object changed) {
Pair<Integer, Object> indexValuePair = (Pair<Integer, Object>) 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());
}
}

View File

@ -28,6 +28,7 @@ import java.util.Collection;
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<T extends Map> extends AbstractCollectionMapper
public MapCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
Class<? extends T> collectionClass, Class<? extends T> 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 @@ public class MapCollectionMapper<T extends Map> extends AbstractCollectionMapper
}
}
protected void mapToMapFromObject(Map<String, Object> 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<String, Object> idData, Map<String, Object> data, Object changed) {
elementComponentData.getComponentMapper().mapToMapFromObject(session, idData, data, ((Map.Entry) changed).getValue());
indexComponentData.getComponentMapper().mapToMapFromObject(session, idData, data, ((Map.Entry) changed).getKey());
}
}

View File

@ -39,8 +39,9 @@ public final class SortedMapCollectionMapper extends MapCollectionMapper<SortedM
public SortedMapCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
Class<? extends SortedMap> collectionClass, Class<? extends SortedMap> 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;
}

View File

@ -39,8 +39,9 @@ public final class SortedSetCollectionMapper extends BasicCollectionMapper<Sorte
public SortedSetCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
Class<? extends SortedSet> collectionClass, Class<? extends SortedSet> 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;
}

View File

@ -34,6 +34,7 @@ import org.hibernate.envers.entities.mapper.id.IdMapper;
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 class ToOneIdMapper extends AbstractToOneMapper {
setPropertyValue(obj, value);
}
public void addMiddleEqualToQuery(Parameters parameters, String idPrefix1, String prefix1, String idPrefix2, String prefix2) {
delegate.addIdsEqualToQuery( parameters, prefix1, delegate, prefix2 );
}
}

View File

@ -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 @@ public interface MiddleComponentMapper {
/**
* Maps from an object to the object's map representation (for an entity - only its id).
* @param session The current session.
* @param idData Map to which composite-id data should be added.
* @param data Map to which data should be added.
* @param obj Object to map from.
*/
void mapToMapFromObject(Map<String, Object> data, Object obj);
void mapToMapFromObject(SessionImplementor session, Map<String, Object> idData, Map<String, Object> 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);
}

View File

@ -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 final class MiddleDummyComponentMapper implements MiddleComponentMapper {
return null;
}
public void mapToMapFromObject(Map<String, Object> data, Object obj) {
public void mapToMapFromObject(SessionImplementor session, Map<String, Object> idData, Map<String, Object> 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) {
}
}

View File

@ -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<String, Object> 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<String, Object> idData, Map<String, Object> 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<PropertyData, PropertyMapper> 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<PropertyData, PropertyMapper> getProperties() {
return delegate.getProperties();
}
}

View File

@ -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 final class MiddleMapKeyIdComponentMapper implements MiddleComponentMappe
return relatedIdMapper.mapToIdFromMap((Map) data.get(verEntCfg.getOriginalIdPropName()));
}
public void mapToMapFromObject(Map<String, Object> data, Object obj) {
public void mapToMapFromObject(SessionImplementor session, Map<String, Object> idData, Map<String, Object> 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.
}
}

View File

@ -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 class MiddleMapKeyPropertyComponentMapper implements MiddleComponentMappe
return ReflectionTools.getGetter(dataObject.getClass(), propertyName, accessType).get(dataObject);
}
public void mapToMapFromObject(Map<String, Object> data, Object obj) {
public void mapToMapFromObject(SessionImplementor session, Map<String, Object> idData, Map<String, Object> 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.
}
}

View File

@ -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 final class MiddleRelatedComponentMapper implements MiddleComponentMapper
return entityInstantiator.createInstanceFromVersionsEntity(relatedIdData.getEntityName(), data, revision);
}
public void mapToMapFromObject(Map<String, Object> data, Object obj) {
relatedIdData.getPrefixedMapper().mapToMapFromEntity(data, obj);
public void mapToMapFromObject(SessionImplementor session, Map<String, Object> idData, Map<String, Object> 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);
}
}

View File

@ -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 final class MiddleSimpleComponentMapper implements MiddleComponentMapper
return ((Map<String, Object>) data.get(verEntCfg.getOriginalIdPropName())).get(propertyName);
}
public void mapToMapFromObject(Map<String, Object> data, Object obj) {
data.put(propertyName, obj);
public void mapToMapFromObject(SessionImplementor session, Map<String, Object> idData, Map<String, Object> 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);
}
}

View File

@ -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 final class MiddleStraightComponentMapper implements MiddleComponentMappe
return data.get(propertyName);
}
public void mapToMapFromObject(Map<String, Object> data, Object obj) {
data.put(propertyName, obj);
}
public void mapToMapFromObject(SessionImplementor session, Map<String, Object> idData, Map<String, Object> 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!");
}
}

View File

@ -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();
}
}

View File

@ -45,15 +45,15 @@ import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants
* 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 final class OneAuditEntityQueryGenerator implements RelationQueryGenerato
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.<String, Object>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;
}
}

View File

@ -44,16 +44,16 @@ import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants
* 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 final class OneEntityQueryGenerator implements RelationQueryGenerator {
// (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.<String, Object>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;
}
}

View File

@ -49,9 +49,8 @@ import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants
* 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 final class ThreeEntityQueryGenerator implements RelationQueryGenerator {
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 final class ThreeEntityQueryGenerator implements RelationQueryGenerator {
// --> 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.<String, Object>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;
}
}

View File

@ -45,16 +45,16 @@ import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants
* 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 final class TwoEntityOneAuditedQueryGenerator implements RelationQueryGen
// --> 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.<String, Object>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;
}
}

View File

@ -47,9 +47,8 @@ import static org.hibernate.envers.entities.mapper.relation.query.QueryConstants
* 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 final class TwoEntityQueryGenerator implements RelationQueryGenerator {
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 final class TwoEntityQueryGenerator implements RelationQueryGenerator {
// --> 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.<String, Object>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;
}
}

View File

@ -156,7 +156,7 @@ public abstract class BaseEnversCollectionEventListener extends BaseEnversEventL
.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.

View File

@ -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:
* <ul>
* <li>For {@link DefaultAuditStrategy} a subquery is created:
* <p><code>e.revision = (SELECT max(...) ...)</code></p>
* </li>
* <li>for {@link ValidityAuditStrategy} the revision-end column is used:
* <p><code>e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)</code></p>
* </li>
* </ul>
*
* @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:
* <ul>
* <li>For {@link DefaultAuditStrategy} a subquery is created:
* <p><code>e.revision = (SELECT max(...) ...)</code></p>
* </li>
* <li>for {@link ValidityAuditStrategy} the revision-end column is used:
* <p><code>e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)</code></p>
* </li>
* </ul>
*
* @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:
* <ul>
* <li>For {@link DefaultAuditStrategy} a subquery is created:
* <p><code>e.revision = (SELECT max(...) ...)</code></p>
* </li>
* <li>for {@link ValidityAuditStrategy} the revision-end column is used:
* <p><code>e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)</code></p>
* </li>
* </ul>
*
* @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:
* <ul>
* <li>For {@link DefaultAuditStrategy} a subquery is created:
* <p><code>e.revision = (SELECT max(...) ...)</code></p>
* </li>
* <li>for {@link ValidityAuditStrategy} the revision-end column is used:
* <p><code>e.revision <= :revision and (e.endRevision > :revision or e.endRevision is null)</code></p>
* </li>
* </ul>
*
* @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);
}

View File

@ -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);
}
}

View File

@ -16,6 +16,7 @@ import java.util.Set;
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.Queryable;
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 @@ public class ValidityAuditStrategy implements AuditStrategy {
}
@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<String, Object> originalId = (Map<String, Object>) persistentCollectionChangeData.getData().get(
originalIdPropName);
final Map<String, Object> originalId = (Map<String, Object>) 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<String, Object> 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<String, Object> dataEntry : persistentCollectionChangeData.getData().entrySet() ) {
if ( !originalIdPropName.equals( dataEntry.getKey() ) ) {
qb.getRootParameters().addWhereWithParam( dataEntry.getKey(), true, "=", dataEntry.getValue() );
}
}
}
}
addEndRevisionNullRestriction(auditCfg, qb.getRootParameters());
final List<Object> l = qb.toQuery(session).setLockOptions(LockOptions.UPGRADE).list();
@ -280,7 +300,7 @@ public class ValidityAuditStrategy implements AuditStrategy {
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);
}

View File

@ -53,7 +53,7 @@ public class PersistentCollectionChangeWorkUnit extends AbstractAuditWorkUnit im
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 class PersistentCollectionChangeWorkUnit extends AbstractAuditWorkUnit im
((Map<String, Object>) persistentCollectionChangeData.getData().get(entitiesCfg.getOriginalIdPropName()))
.put(entitiesCfg.getRevisionFieldName(), revisionData);
auditStrategy.performCollectionChange(session, verCfg, persistentCollectionChangeData, revisionData);
auditStrategy.performCollectionChange(session, getEntityName(), referencingPropertyName, verCfg, persistentCollectionChangeData, revisionData);
}
}

View File

@ -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 + ")";
}
}

View File

@ -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<Component3> componentList = new ArrayList<Component3>();
public EmbeddableListEntity1() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<Component3> getComponentList() {
return componentList;
}
public void setComponentList(List<Component3> 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 + ")";
}
}

View File

@ -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<ManyToOneEagerComponent> componentList = new ArrayList<ManyToOneEagerComponent>();
public EmbeddableListEntity2() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<ManyToOneEagerComponent> getComponentList() {
return componentList;
}
public void setComponentList(List<ManyToOneEagerComponent> 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 + ")";
}
}

View File

@ -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<String, Component3> componentMap;
public EmbeddableMapEntity() {
componentMap = new HashMap<String, Component3>();
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Map<String, Component3> getComponentMap() {
return componentMap;
}
public void setComponentMap(Map<String, Component3> 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 + ")";
}
}

View File

@ -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<Component3> componentSet = new HashSet<Component3>();
public EmbeddableSetEntity() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Set<Component3> getComponentSet() {
return componentSet;
}
public void setComponentSet(Set<Component3> 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 + ')';
}
}

View File

@ -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 + "]";
}
}

View File

@ -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 + "]";
}
}

View File

@ -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;

View File

@ -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 + ")";
}
}

View File

@ -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() );
}
}

View File

@ -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<Name> names = new HashSet<Name>();
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<Name> getNames() {
return names;
}
public void setNames(Set<Name> names) {
this.names = names;
}
}

View File

@ -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() );
}
}

View File

@ -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 ) );
}
}

View File

@ -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() );
}
}

View File

@ -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() );
}
}

View File

@ -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;
}
}

View File

@ -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 ) );
}
}