HHH-4694:

- support for indexed "fake" bidirectional relations
- adding a field-calculation-phase, after all metadata is read from annotations, but before any audit entities generation is done

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18236 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Adam Warski 2009-12-16 11:09:34 +00:00
parent 5a6686810b
commit a90cf3e5eb
22 changed files with 683 additions and 104 deletions

View File

@ -1,7 +1,12 @@
package org.hibernate.envers.configuration; package org.hibernate.envers.configuration;
import org.hibernate.envers.configuration.metadata.reader.ClassAuditingData; import org.hibernate.envers.configuration.metadata.reader.ClassAuditingData;
import org.hibernate.envers.configuration.metadata.reader.PropertyAuditingData;
import org.hibernate.envers.tools.MappingTools;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.MappingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -13,6 +18,8 @@ import java.util.LinkedHashMap;
* @author Adam Warski (adam at warski dot org) * @author Adam Warski (adam at warski dot org)
*/ */
public class ClassesAuditingData { public class ClassesAuditingData {
private static final Logger log = LoggerFactory.getLogger(ClassesAuditingData.class);
private final Map<String, ClassAuditingData> entityNameToAuditingData = new HashMap<String, ClassAuditingData>(); private final Map<String, ClassAuditingData> entityNameToAuditingData = new HashMap<String, ClassAuditingData>();
private final Map<PersistentClass, ClassAuditingData> persistentClassToAuditingData = new LinkedHashMap<PersistentClass, ClassAuditingData>(); private final Map<PersistentClass, ClassAuditingData> persistentClassToAuditingData = new LinkedHashMap<PersistentClass, ClassAuditingData>();
@ -40,4 +47,50 @@ public class ClassesAuditingData {
public ClassAuditingData getClassAuditingData(String entityName) { public ClassAuditingData getClassAuditingData(String entityName) {
return entityNameToAuditingData.get(entityName); return entityNameToAuditingData.get(entityName);
} }
/**
* After all meta-data is read, updates calculated fields. This includes:
* <ul>
* <li>setting {@code forceInsertable} to {@code true} for properties specified by {@code @AuditMappedBy}</li>
* </ul>
*/
public void updateCalculatedFields() {
for (Map.Entry<PersistentClass, ClassAuditingData> classAuditingDataEntry : persistentClassToAuditingData.entrySet()) {
PersistentClass pc = classAuditingDataEntry.getKey();
ClassAuditingData classAuditingData = classAuditingDataEntry.getValue();
for (String propertyName : classAuditingData.getPropertyNames()) {
PropertyAuditingData propertyAuditingData = classAuditingData.getPropertyAuditingData(propertyName);
// If a property had the @AuditMappedBy annotation, setting the referenced fields to be always insertable.
if (propertyAuditingData.getAuditMappedBy() != null) {
String referencedEntityName = MappingTools.getReferencedEntityName(pc.getProperty(propertyName).getValue());
ClassAuditingData referencedClassAuditingData = entityNameToAuditingData.get(referencedEntityName);
forcePropertyInsertable(referencedClassAuditingData, propertyAuditingData.getAuditMappedBy(),
pc.getEntityName(), referencedEntityName);
forcePropertyInsertable(referencedClassAuditingData, propertyAuditingData.getPositionMappedBy(),
pc.getEntityName(), referencedEntityName);
}
}
}
}
private void forcePropertyInsertable(ClassAuditingData classAuditingData, String propertyName,
String entityName, String referencedEntityName) {
if (propertyName != null) {
if (classAuditingData.getPropertyAuditingData(propertyName) == null) {
throw new MappingException("@AuditMappedBy points to a property that doesn't exist: " +
referencedEntityName + "." + propertyName);
}
log.debug("Non-insertable property " + referencedEntityName + "." + propertyName +
" will be made insertable because a matching @AuditMappedBy was found in the " +
entityName + " entity.");
classAuditingData
.getPropertyAuditingData(propertyName)
.setForceInsertable(true);
}
}
} }

View File

@ -80,6 +80,9 @@ public class EntitiesConfigurator {
classesAuditingData.addClassAuditingData(pc, auditData); classesAuditingData.addClassAuditingData(pc, auditData);
} }
// Now that all information is read we can update the calculated fields.
classesAuditingData.updateCalculatedFields();
AuditMetadataGenerator auditMetaGen = new AuditMetadataGenerator(cfg, globalCfg, verEntCfg, AuditMetadataGenerator auditMetaGen = new AuditMetadataGenerator(cfg, globalCfg, verEntCfg,
revisionInfoRelationMapping, auditEntityNameRegister, classesAuditingData); revisionInfoRelationMapping, auditEntityNameRegister, classesAuditingData);

View File

@ -66,7 +66,7 @@ public final class BasicMetadataGenerator {
Value value, SimpleMapperBuilder mapper, boolean insertable, boolean key) { Value value, SimpleMapperBuilder mapper, boolean insertable, boolean key) {
if (parent != null) { if (parent != null) {
Element prop_mapping = MetadataTools.addProperty(parent, propertyAuditingData.getName(), Element prop_mapping = MetadataTools.addProperty(parent, propertyAuditingData.getName(),
value.getType().getName(), insertable, key); value.getType().getName(), propertyAuditingData.isForceInsertable() || insertable, key);
MetadataTools.addColumns(prop_mapping, (Iterator<Column>) value.getColumnIterator()); MetadataTools.addColumns(prop_mapping, (Iterator<Column>) value.getColumnIterator());
} }

View File

@ -43,13 +43,10 @@ import org.hibernate.envers.entities.IdMappingData;
import org.hibernate.envers.entities.PropertyData; import org.hibernate.envers.entities.PropertyData;
import org.hibernate.envers.entities.mapper.CompositeMapperBuilder; import org.hibernate.envers.entities.mapper.CompositeMapperBuilder;
import org.hibernate.envers.entities.mapper.PropertyMapper; import org.hibernate.envers.entities.mapper.PropertyMapper;
import org.hibernate.envers.entities.mapper.SinglePropertyMapper;
import org.hibernate.envers.entities.mapper.id.IdMapper; import org.hibernate.envers.entities.mapper.id.IdMapper;
import org.hibernate.envers.entities.mapper.relation.*; import org.hibernate.envers.entities.mapper.relation.*;
import org.hibernate.envers.entities.mapper.relation.component.MiddleDummyComponentMapper; import org.hibernate.envers.entities.mapper.relation.component.*;
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;
import org.hibernate.envers.entities.mapper.relation.component.MiddleSimpleComponentMapper;
import org.hibernate.envers.entities.mapper.relation.lazy.proxy.ListProxy; import org.hibernate.envers.entities.mapper.relation.lazy.proxy.ListProxy;
import org.hibernate.envers.entities.mapper.relation.lazy.proxy.MapProxy; import org.hibernate.envers.entities.mapper.relation.lazy.proxy.MapProxy;
import org.hibernate.envers.entities.mapper.relation.lazy.proxy.SetProxy; import org.hibernate.envers.entities.mapper.relation.lazy.proxy.SetProxy;
@ -68,7 +65,6 @@ import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value; import org.hibernate.mapping.Value;
import org.hibernate.type.BagType; import org.hibernate.type.BagType;
import org.hibernate.type.ListType; import org.hibernate.type.ListType;
@ -132,17 +128,7 @@ public final class CollectionMetadataGenerator {
throw new MappingException("Unable to read auditing configuration for " + referencingEntityName + "!"); throw new MappingException("Unable to read auditing configuration for " + referencingEntityName + "!");
} }
referencedEntityName = getReferencedEntityName(propertyValue.getElement()); referencedEntityName = MappingTools.getReferencedEntityName(propertyValue.getElement());
}
private String getReferencedEntityName(Value value) {
if (value instanceof ToOne) {
return ((ToOne) value).getReferencedEntityName();
} else if (value instanceof OneToMany) {
return ((OneToMany) value).getReferencedEntityName();
} else {
return null;
}
} }
void addCollection() { void addCollection() {
@ -206,10 +192,8 @@ public final class CollectionMetadataGenerator {
propertyAuditingData.getPropertyData(), propertyAuditingData.getPropertyData(),
referencingIdData, queryGenerator); referencingIdData, queryGenerator);
// Checking the type of the collection and adding an appropriate mapper.
addMapper(commonCollectionMapperData, elementComponentData, indexComponentData);
PropertyMapper fakeBidirectionalRelationMapper; PropertyMapper fakeBidirectionalRelationMapper;
PropertyMapper fakeBidirectionalRelationIndexMapper;
if (fakeOneToManyBidirectional) { if (fakeOneToManyBidirectional) {
// In case of a fake many-to-one bidirectional relation, we have to generate a mapper which maps // In case of a fake many-to-one bidirectional relation, we have to generate a mapper which maps
// the mapped-by property name to the id of the related entity (which is the owner of the collection). // the mapped-by property name to the id of the related entity (which is the owner of the collection).
@ -225,13 +209,29 @@ public final class CollectionMetadataGenerator {
// when constructing the PropertyData. // when constructing the PropertyData.
new PropertyData(auditMappedBy, null, null, null), new PropertyData(auditMappedBy, null, null, null),
referencedEntityName, false); referencedEntityName, false);
// Checking if there's an index defined. If so, adding a mapper for it.
if (propertyAuditingData.getPositionMappedBy() != null) {
String positionMappedBy = propertyAuditingData.getPositionMappedBy();
fakeBidirectionalRelationIndexMapper = new SinglePropertyMapper(new PropertyData(positionMappedBy, null, null, null));
// Also, overwriting the index component data to properly read the index.
indexComponentData = new MiddleComponentData(new MiddleStraightComponentMapper(positionMappedBy), 0);
} else {
fakeBidirectionalRelationIndexMapper = null;
}
} else { } else {
fakeBidirectionalRelationMapper = null; fakeBidirectionalRelationMapper = null;
fakeBidirectionalRelationIndexMapper = null;
} }
// Checking the type of the collection and adding an appropriate mapper.
addMapper(commonCollectionMapperData, elementComponentData, indexComponentData);
// Storing information about this relation. // Storing information about this relation.
referencingEntityConfiguration.addToManyNotOwningRelation(propertyName, mappedBy, referencingEntityConfiguration.addToManyNotOwningRelation(propertyName, mappedBy,
referencedEntityName, referencingIdData.getPrefixedMapper(), fakeBidirectionalRelationMapper); referencedEntityName, referencingIdData.getPrefixedMapper(), fakeBidirectionalRelationMapper,
fakeBidirectionalRelationIndexMapper);
} }
/** /**
@ -257,7 +257,7 @@ public final class CollectionMetadataGenerator {
if (value.getElement() instanceof OneToMany && !value.isInverse()) { if (value.getElement() instanceof OneToMany && !value.isInverse()) {
// This must be a @JoinColumn+@OneToMany mapping. Generating the table name, as Hibernate doesn't use a // This must be a @JoinColumn+@OneToMany mapping. Generating the table name, as Hibernate doesn't use a
// middle table for mapping this relation. // middle table for mapping this relation.
return StringTools.getLastComponent(entityName) + "_" + StringTools.getLastComponent(getReferencedEntityName(value.getElement())); return StringTools.getLastComponent(entityName) + "_" + StringTools.getLastComponent(MappingTools.getReferencedEntityName(value.getElement()));
} else { } else {
// Hibernate uses a middle table for mapping this relation, so we get it's name directly. // Hibernate uses a middle table for mapping this relation, so we get it's name directly.
return value.getCollectionTable().getName(); return value.getCollectionTable().getName();
@ -421,7 +421,7 @@ public final class CollectionMetadataGenerator {
if (type instanceof ManyToOneType) { if (type instanceof ManyToOneType) {
String prefixRelated = prefix + "_"; String prefixRelated = prefix + "_";
String referencedEntityName = getReferencedEntityName(value); String referencedEntityName = MappingTools.getReferencedEntityName(value);
IdMappingData referencedIdMapping = mainGenerator.getReferencedIdMappingData(referencingEntityName, IdMappingData referencedIdMapping = mainGenerator.getReferencedIdMappingData(referencingEntityName,
referencedEntityName, propertyAuditingData, true); referencedEntityName, propertyAuditingData, true);
@ -447,7 +447,7 @@ public final class CollectionMetadataGenerator {
} else { } else {
// Last but one parameter: collection components are always insertable // Last but one parameter: collection components are always insertable
boolean mapped = mainGenerator.getBasicMetadataGenerator().addBasic(xmlMapping, boolean mapped = mainGenerator.getBasicMetadataGenerator().addBasic(xmlMapping,
new PropertyAuditingData(prefix, "field", ModificationStore.FULL, RelationTargetAuditMode.AUDITED, null, null), new PropertyAuditingData(prefix, "field", ModificationStore.FULL, RelationTargetAuditMode.AUDITED, null, null, false),
value, null, true, true); value, null, true, true);
if (mapped) { if (mapped) {
@ -533,31 +533,14 @@ public final class CollectionMetadataGenerator {
return middleEntityXmlId; return middleEntityXmlId;
} }
private String getMappedByCommon(PersistentClass referencedClass) {
// If there's an @AuditMappedBy specified, returning it directly.
String auditMappedBy = propertyAuditingData.getAuditMappedBy();
if (auditMappedBy != null) {
// Checking that the property exists.
try {
referencedClass.getProperty(auditMappedBy);
} catch (MappingException me) {
throw new MappingException("@AuditMappedBy points to a property that can be read: " +
referencedClass.getEntityName() + "." + auditMappedBy, me);
}
return auditMappedBy;
}
return null;
}
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
private String getMappedBy(Collection collectionValue) { private String getMappedBy(Collection collectionValue) {
PersistentClass referencedClass = ((OneToMany) collectionValue.getElement()).getAssociatedClass(); PersistentClass referencedClass = ((OneToMany) collectionValue.getElement()).getAssociatedClass();
String mappedByCommon = getMappedByCommon(referencedClass); // If there's an @AuditMappedBy specified, returning it directly.
if (mappedByCommon != null) { String auditMappedBy = propertyAuditingData.getAuditMappedBy();
return mappedByCommon; if (auditMappedBy != null) {
return auditMappedBy;
} }
Iterator<Property> assocClassProps = referencedClass.getPropertyIterator(); Iterator<Property> assocClassProps = referencedClass.getPropertyIterator();
@ -577,9 +560,10 @@ public final class CollectionMetadataGenerator {
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
private String getMappedBy(Table collectionTable, PersistentClass referencedClass) { private String getMappedBy(Table collectionTable, PersistentClass referencedClass) {
String mappedByCommon = getMappedByCommon(referencedClass); // If there's an @AuditMappedBy specified, returning it directly.
if (mappedByCommon != null) { String auditMappedBy = propertyAuditingData.getAuditMappedBy();
return mappedByCommon; if (auditMappedBy != null) {
return auditMappedBy;
} }
Iterator<Property> properties = referencedClass.getPropertyIterator(); Iterator<Property> properties = referencedClass.getPropertyIterator();

View File

@ -139,6 +139,6 @@ public final class IdMetadataGenerator {
private PropertyAuditingData getIdPersistentPropertyAuditingData(Property property) { private PropertyAuditingData getIdPersistentPropertyAuditingData(Property property) {
return new PropertyAuditingData(property.getName(), property.getPropertyAccessorName(), return new PropertyAuditingData(property.getName(), property.getPropertyAccessorName(),
ModificationStore.FULL, RelationTargetAuditMode.AUDITED, null, null); ModificationStore.FULL, RelationTargetAuditMode.AUDITED, null, null, false);
} }
} }

View File

@ -24,6 +24,8 @@
package org.hibernate.envers.configuration.metadata; package org.hibernate.envers.configuration.metadata;
import org.dom4j.Element; import org.dom4j.Element;
import org.hibernate.MappingException;
import org.hibernate.envers.configuration.metadata.reader.PropertyAuditingData;
import org.hibernate.envers.entities.EntityConfiguration; import org.hibernate.envers.entities.EntityConfiguration;
import org.hibernate.envers.entities.IdMappingData; import org.hibernate.envers.entities.IdMappingData;
import org.hibernate.envers.entities.PropertyData; import org.hibernate.envers.entities.PropertyData;
@ -31,24 +33,16 @@ import org.hibernate.envers.entities.mapper.CompositeMapperBuilder;
import org.hibernate.envers.entities.mapper.id.IdMapper; import org.hibernate.envers.entities.mapper.id.IdMapper;
import org.hibernate.envers.entities.mapper.relation.OneToOneNotOwningMapper; import org.hibernate.envers.entities.mapper.relation.OneToOneNotOwningMapper;
import org.hibernate.envers.entities.mapper.relation.ToOneIdMapper; import org.hibernate.envers.entities.mapper.relation.ToOneIdMapper;
import org.hibernate.envers.configuration.metadata.reader.PropertyAuditingData;
import org.hibernate.envers.configuration.metadata.reader.ClassAuditingData;
import org.hibernate.envers.tools.MappingTools; import org.hibernate.envers.tools.MappingTools;
import org.hibernate.MappingException;
import org.hibernate.mapping.OneToOne; import org.hibernate.mapping.OneToOne;
import org.hibernate.mapping.ToOne; import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value; import org.hibernate.mapping.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Generates metadata for to-one relations (reference-valued properties). * Generates metadata for to-one relations (reference-valued properties).
* @author Adam Warski (adam at warski dot org) * @author Adam Warski (adam at warski dot org)
*/ */
public final class ToOneRelationMetadataGenerator { public final class ToOneRelationMetadataGenerator {
private static final Logger log = LoggerFactory.getLogger(ToOneRelationMetadataGenerator.class);
private final AuditMetadataGenerator mainGenerator; private final AuditMetadataGenerator mainGenerator;
ToOneRelationMetadataGenerator(AuditMetadataGenerator auditMetadataGenerator) { ToOneRelationMetadataGenerator(AuditMetadataGenerator auditMetadataGenerator) {
@ -79,23 +73,12 @@ public final class ToOneRelationMetadataGenerator {
// the entity that didn't involve the relation, it's value will then be stored properly. In case of changes // the entity that didn't involve the relation, it's value will then be stored properly. In case of changes
// to the entity that did involve the relation, it's the responsibility of the collection side to store the // to the entity that did involve the relation, it's the responsibility of the collection side to store the
// proper data. // proper data.
boolean nonInsertableFake = false; boolean nonInsertableFake;
if (!insertable) { if (!insertable && propertyAuditingData.isForceInsertable()) {
ClassAuditingData referencedAuditingData = mainGenerator.getClassesAuditingData().getClassAuditingData(referencedEntityName); nonInsertableFake = true;
insertable = true;
// Looking through the properties of the referenced entity to find the right property. } else {
for (String referencedPropertyName : referencedAuditingData.getPropertyNames()) { nonInsertableFake = false;
String auditMappedBy = referencedAuditingData.getPropertyAuditingData(referencedPropertyName).getAuditMappedBy();
if (propertyAuditingData.getName().equals(auditMappedBy)) {
log.debug("Non-insertable property " + entityName + "." + propertyAuditingData.getName() +
" will be made insertable because a matching @AuditMappedBy was found in the " +
referencedEntityName + " entity.");
insertable = true;
nonInsertableFake = true;
break;
}
}
} }
// Adding an element to the mapping corresponding to the references entity id's // Adding an element to the mapping corresponding to the references entity id's

View File

@ -48,13 +48,15 @@ public class PropertyAuditingData {
private RelationTargetAuditMode relationTargetAuditMode; private RelationTargetAuditMode relationTargetAuditMode;
private String auditMappedBy; private String auditMappedBy;
private String positionMappedBy; private String positionMappedBy;
private boolean forceInsertable;
public PropertyAuditingData() { public PropertyAuditingData() {
} }
public PropertyAuditingData(String name, String accessType, ModificationStore store, public PropertyAuditingData(String name, String accessType, ModificationStore store,
RelationTargetAuditMode relationTargetAuditMode, RelationTargetAuditMode relationTargetAuditMode,
String auditMappedBy, String positionMappedBy) { String auditMappedBy, String positionMappedBy,
boolean forceInsertable) {
this.name = name; this.name = name;
this.beanName = name; this.beanName = name;
this.accessType = accessType; this.accessType = accessType;
@ -62,6 +64,7 @@ public class PropertyAuditingData {
this.relationTargetAuditMode = relationTargetAuditMode; this.relationTargetAuditMode = relationTargetAuditMode;
this.auditMappedBy = auditMappedBy; this.auditMappedBy = auditMappedBy;
this.positionMappedBy = positionMappedBy; this.positionMappedBy = positionMappedBy;
this.forceInsertable = forceInsertable;
} }
public String getName() { public String getName() {
@ -136,6 +139,14 @@ public class PropertyAuditingData {
this.positionMappedBy = positionMappedBy; this.positionMappedBy = positionMappedBy;
} }
public boolean isForceInsertable() {
return forceInsertable;
}
public void setForceInsertable(boolean forceInsertable) {
this.forceInsertable = forceInsertable;
}
public void addAuditingOverride(AuditOverride annotation) { public void addAuditingOverride(AuditOverride annotation) {
if (annotation != null) { if (annotation != null) {
String overrideName = annotation.name(); String overrideName = annotation.name();

View File

@ -53,29 +53,31 @@ public class EntityConfiguration {
public void addToOneRelation(String fromPropertyName, String toEntityName, IdMapper idMapper, boolean insertable) { public void addToOneRelation(String fromPropertyName, String toEntityName, IdMapper idMapper, boolean insertable) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_ONE, relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_ONE,
toEntityName, null, idMapper, null, insertable)); toEntityName, null, idMapper, null, null, insertable));
} }
public void addToOneNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName, public void addToOneNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName,
IdMapper idMapper) { IdMapper idMapper) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_ONE_NOT_OWNING, relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_ONE_NOT_OWNING,
toEntityName, mappedByPropertyName, idMapper, null, true)); toEntityName, mappedByPropertyName, idMapper, null, null, true));
} }
public void addToManyNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName, public void addToManyNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName,
IdMapper idMapper, PropertyMapper fakeBidirectionalRelationMapper) { IdMapper idMapper, PropertyMapper fakeBidirectionalRelationMapper,
PropertyMapper fakeBidirectionalRelationIndexMapper) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_NOT_OWNING, relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_NOT_OWNING,
toEntityName, mappedByPropertyName, idMapper, fakeBidirectionalRelationMapper, true)); toEntityName, mappedByPropertyName, idMapper, fakeBidirectionalRelationMapper,
fakeBidirectionalRelationIndexMapper, true));
} }
public void addToManyMiddleRelation(String fromPropertyName, String toEntityName) { public void addToManyMiddleRelation(String fromPropertyName, String toEntityName) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_MIDDLE, relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_MIDDLE,
toEntityName, null, null, null, true)); toEntityName, null, null, null, null, true));
} }
public void addToManyMiddleNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName) { public void addToManyMiddleNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_MIDDLE_NOT_OWNING, relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_MIDDLE_NOT_OWNING,
toEntityName, mappedByPropertyName, null, null, true)); toEntityName, mappedByPropertyName, null, null, null, true));
} }
public boolean isRelation(String propertyName) { public boolean isRelation(String propertyName) {

View File

@ -36,18 +36,21 @@ public class RelationDescription {
private final String mappedByPropertyName; private final String mappedByPropertyName;
private final IdMapper idMapper; private final IdMapper idMapper;
private final PropertyMapper fakeBidirectionalRelationMapper; private final PropertyMapper fakeBidirectionalRelationMapper;
private final PropertyMapper fakeBidirectionalRelationIndexMapper;
private final boolean insertable; private final boolean insertable;
private boolean bidirectional; private boolean bidirectional;
public RelationDescription(String fromPropertyName, RelationType relationType, String toEntityName, public RelationDescription(String fromPropertyName, RelationType relationType, String toEntityName,
String mappedByPropertyName, IdMapper idMapper, String mappedByPropertyName, IdMapper idMapper,
PropertyMapper fakeBidirectionalRelationMapper, boolean insertable) { PropertyMapper fakeBidirectionalRelationMapper,
PropertyMapper fakeBidirectionalRelationIndexMapper, boolean insertable) {
this.fromPropertyName = fromPropertyName; this.fromPropertyName = fromPropertyName;
this.relationType = relationType; this.relationType = relationType;
this.toEntityName = toEntityName; this.toEntityName = toEntityName;
this.mappedByPropertyName = mappedByPropertyName; this.mappedByPropertyName = mappedByPropertyName;
this.idMapper = idMapper; this.idMapper = idMapper;
this.fakeBidirectionalRelationMapper = fakeBidirectionalRelationMapper; this.fakeBidirectionalRelationMapper = fakeBidirectionalRelationMapper;
this.fakeBidirectionalRelationIndexMapper = fakeBidirectionalRelationIndexMapper;
this.insertable = insertable; this.insertable = insertable;
this.bidirectional = false; this.bidirectional = false;
@ -77,6 +80,10 @@ public class RelationDescription {
return fakeBidirectionalRelationMapper; return fakeBidirectionalRelationMapper;
} }
public PropertyMapper getFakeBidirectionalRelationIndexMapper() {
return fakeBidirectionalRelationIndexMapper;
}
public boolean isInsertable() { public boolean isInsertable() {
return insertable; return insertable;
} }

View File

@ -23,10 +23,12 @@
*/ */
package org.hibernate.envers.entities.mapper; package org.hibernate.envers.entities.mapper;
import org.hibernate.envers.tools.Pair;
import java.util.Map; import java.util.Map;
/** /**
* Data describing the change of a single object in a persisten collection (when the object was added, removed or * Data describing the change of a single object in a persistent collection (when the object was added, removed or
* modified in the collection). * modified in the collection).
* @author Adam Warski (adam at warski dot org) * @author Adam Warski (adam at warski dot org)
*/ */
@ -54,10 +56,32 @@ public class PersistentCollectionChangeData {
} }
/** /**
* For use by bi-directional associations.
* @return The affected element, which was changed (added, removed, modified) in the collection. * @return The affected element, which was changed (added, removed, modified) in the collection.
*/ */
public Object getChangedElement() { public Object getChangedElement() {
if (changedElement instanceof Pair) {
return ((Pair) changedElement).getSecond();
}
if (changedElement instanceof Map.Entry) {
return ((Map.Entry) changedElement).getValue();
}
return changedElement; return changedElement;
} }
/**
* @return Index of the affected element, or {@code null} if the collection isn't indexed.
*/
public Object getChangedElementIndex() {
if (changedElement instanceof Pair) {
return ((Pair) changedElement).getFirst();
}
if (changedElement instanceof Map.Entry) {
return ((Map.Entry) changedElement).getKey();
}
return null;
}
} }

View File

@ -47,6 +47,10 @@ import org.hibernate.HibernateException;
public class SinglePropertyMapper implements PropertyMapper, SimpleMapperBuilder { public class SinglePropertyMapper implements PropertyMapper, SimpleMapperBuilder {
private PropertyData propertyData; private PropertyData propertyData;
public SinglePropertyMapper(PropertyData propertyData) {
this.propertyData = propertyData;
}
public SinglePropertyMapper() { } public SinglePropertyMapper() { }
public void add(PropertyData propertyData) { public void add(PropertyData propertyData) {

View File

@ -70,7 +70,6 @@ public abstract class AbstractCollectionMapper<T> implements PropertyMapper {
protected abstract Collection getNewCollectionContent(PersistentCollection newCollection); protected abstract Collection getNewCollectionContent(PersistentCollection newCollection);
protected abstract Collection getOldCollectionContent(Serializable oldCollection); protected abstract Collection getOldCollectionContent(Serializable oldCollection);
protected abstract Object getElement(Object changedObject);
/** /**
* Maps the changed collection element to the given map. * Maps the changed collection element to the given map.
@ -87,7 +86,7 @@ public abstract class AbstractCollectionMapper<T> implements PropertyMapper {
entityData.put(commonCollectionMapperData.getVerEntCfg().getOriginalIdPropName(), originalId); entityData.put(commonCollectionMapperData.getVerEntCfg().getOriginalIdPropName(), originalId);
collectionChanges.add(new PersistentCollectionChangeData( collectionChanges.add(new PersistentCollectionChangeData(
commonCollectionMapperData.getVersionsMiddleEntityName(), entityData, getElement(changedObj))); commonCollectionMapperData.getVersionsMiddleEntityName(), entityData, changedObj));
// Mapping the collection owner's id. // Mapping the collection owner's id.
commonCollectionMapperData.getReferencingIdData().getPrefixedMapper().mapToMapFromId(originalId, id); commonCollectionMapperData.getReferencingIdData().getPrefixedMapper().mapToMapFromId(originalId, id);

View File

@ -71,8 +71,4 @@ public final class BasicCollectionMapper<T extends Collection> extends AbstractC
protected void mapToMapFromObject(Map<String, Object> data, Object changed) { protected void mapToMapFromObject(Map<String, Object> data, Object changed) {
elementComponentData.getComponentMapper().mapToMapFromObject(data, changed); elementComponentData.getComponentMapper().mapToMapFromObject(data, changed);
} }
protected Object getElement(Object changedObject) {
return changedObject;
}
} }

View File

@ -83,9 +83,4 @@ public final class ListCollectionMapper extends AbstractCollectionMapper<List> i
elementComponentData.getComponentMapper().mapToMapFromObject(data, indexValuePair.getSecond()); elementComponentData.getComponentMapper().mapToMapFromObject(data, indexValuePair.getSecond());
indexComponentData.getComponentMapper().mapToMapFromObject(data, indexValuePair.getFirst()); indexComponentData.getComponentMapper().mapToMapFromObject(data, indexValuePair.getFirst());
} }
@SuppressWarnings({"unchecked"})
protected Object getElement(Object changedObject) {
return ((Pair<Integer, Object>) changedObject).getFirst();
}
} }

View File

@ -76,8 +76,4 @@ public final class MapCollectionMapper<T extends Map> extends AbstractCollection
elementComponentData.getComponentMapper().mapToMapFromObject(data, ((Map.Entry) changed).getValue()); elementComponentData.getComponentMapper().mapToMapFromObject(data, ((Map.Entry) changed).getValue());
indexComponentData.getComponentMapper().mapToMapFromObject(data, ((Map.Entry) changed).getKey()); indexComponentData.getComponentMapper().mapToMapFromObject(data, ((Map.Entry) changed).getKey());
} }
protected Object getElement(Object changedObject) {
return ((Map.Entry) changedObject).getValue();
}
} }

View File

@ -0,0 +1,56 @@
/*
* 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 org.hibernate.envers.entities.EntityInstantiator;
import org.hibernate.envers.tools.query.Parameters;
import java.util.Map;
/**
* A mapper for reading and writing a property straight to/from maps. This mapper cannot be used with middle tables,
* but only with "fake" bidirectional indexed relations.
* @author Adam Warski (adam at warski dot org)
*/
public final class MiddleStraightComponentMapper implements MiddleComponentMapper {
private final String propertyName;
public MiddleStraightComponentMapper(String propertyName) {
this.propertyName = propertyName;
}
@SuppressWarnings({"unchecked"})
public Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map<String, Object> data,
Object dataObject, Number revision) {
return data.get(propertyName);
}
public void mapToMapFromObject(Map<String, Object> data, Object obj) {
data.put(propertyName, obj);
}
public void addMiddleEqualToQuery(Parameters parameters, String prefix1, String prefix2) {
throw new UnsupportedOperationException("Cannot use this mapper with a middle table!");
}
}

View File

@ -239,7 +239,8 @@ public class AuditEventListener implements PostInsertEventListener, PostUpdateEv
relatedId, relatedObj); relatedId, relatedObj);
verSync.addWorkUnit(new FakeBidirectionalRelationWorkUnit(event.getSession(), relatedEntityName, verCfg, verSync.addWorkUnit(new FakeBidirectionalRelationWorkUnit(event.getSession(), relatedEntityName, verCfg,
relatedId, referencingPropertyName, event.getAffectedOwnerOrNull(), rd, revType, nestedWorkUnit)); relatedId, referencingPropertyName, event.getAffectedOwnerOrNull(), rd, revType,
changeData.getChangedElementIndex(), nestedWorkUnit));
} }
// We also have to generate a collection change work unit for the owning entity. // We also have to generate a collection change work unit for the owning entity.

View File

@ -28,13 +28,14 @@ public class FakeBidirectionalRelationWorkUnit extends AbstractAuditWorkUnit imp
AuditConfiguration verCfg, Serializable id, AuditConfiguration verCfg, Serializable id,
String referencingPropertyName, Object owningEntity, String referencingPropertyName, Object owningEntity,
RelationDescription rd, RevisionType revisionType, RelationDescription rd, RevisionType revisionType,
Object index,
AuditWorkUnit nestedWorkUnit) { AuditWorkUnit nestedWorkUnit) {
super(sessionImplementor, entityName, verCfg, id); super(sessionImplementor, entityName, verCfg, id);
this.nestedWorkUnit = nestedWorkUnit; this.nestedWorkUnit = nestedWorkUnit;
// Adding the change for the relation. // Adding the change for the relation.
fakeRelationChanges = new HashMap<String, FakeRelationChange>(); fakeRelationChanges = new HashMap<String, FakeRelationChange>();
fakeRelationChanges.put(referencingPropertyName, new FakeRelationChange(owningEntity, rd, revisionType)); fakeRelationChanges.put(referencingPropertyName, new FakeRelationChange(owningEntity, rd, revisionType, index));
} }
public FakeBidirectionalRelationWorkUnit(FakeBidirectionalRelationWorkUnit original, public FakeBidirectionalRelationWorkUnit(FakeBidirectionalRelationWorkUnit original,
@ -134,11 +135,14 @@ public class FakeBidirectionalRelationWorkUnit extends AbstractAuditWorkUnit imp
private final Object owningEntity; private final Object owningEntity;
private final RelationDescription rd; private final RelationDescription rd;
private final RevisionType revisionType; private final RevisionType revisionType;
private final Object index;
public FakeRelationChange(Object owningEntity, RelationDescription rd, RevisionType revisionType) { public FakeRelationChange(Object owningEntity, RelationDescription rd, RevisionType revisionType,
Object index) {
this.owningEntity = owningEntity; this.owningEntity = owningEntity;
this.rd = rd; this.rd = rd;
this.revisionType = revisionType; this.revisionType = revisionType;
this.index = index;
} }
public RevisionType getRevisionType() { public RevisionType getRevisionType() {
@ -150,6 +154,12 @@ public class FakeBidirectionalRelationWorkUnit extends AbstractAuditWorkUnit imp
// new owner will in fact be null. // new owner will in fact be null.
rd.getFakeBidirectionalRelationMapper().mapToMapFromEntity(sessionImplementor, data, rd.getFakeBidirectionalRelationMapper().mapToMapFromEntity(sessionImplementor, data,
revisionType == RevisionType.DEL ? null : owningEntity, null); revisionType == RevisionType.DEL ? null : owningEntity, null);
// Also mapping the index, if the collection is indexed.
if (rd.getFakeBidirectionalRelationIndexMapper() != null) {
rd.getFakeBidirectionalRelationIndexMapper().mapToMapFromEntity(sessionImplementor, data,
revisionType == RevisionType.DEL ? null : index, null);
}
} }
public static FakeRelationChange merge(FakeRelationChange first, FakeRelationChange second) { public static FakeRelationChange merge(FakeRelationChange first, FakeRelationChange second) {

View File

@ -1,5 +1,10 @@
package org.hibernate.envers.tools; package org.hibernate.envers.tools;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
/** /**
* @author Adam Warski (adam at warski dot org) * @author Adam Warski (adam at warski dot org)
*/ */
@ -20,4 +25,16 @@ public class MappingTools {
public static String createToOneRelationPrefix(String referencePropertyName) { public static String createToOneRelationPrefix(String referencePropertyName) {
return referencePropertyName + "_"; return referencePropertyName + "_";
} }
public static String getReferencedEntityName(Value value) {
if (value instanceof ToOne) {
return ((ToOne) value).getReferencedEntityName();
} else if (value instanceof OneToMany) {
return ((OneToMany) value).getReferencedEntityName();
} else if (value instanceof Collection) {
return getReferencedEntityName(((Collection) value).getElement());
}
return null;
}
} }

View File

@ -0,0 +1,96 @@
package org.hibernate.envers.test.entities.onetomany.detached;
import org.hibernate.envers.Audited;
import javax.persistence.*;
/**
* Entity for {@link org.hibernate.envers.test.integration.onetomany.detached.IndexedJoinColumnBidirectionalList} test.
* Owned side of the relation.
* @author Adam Warski (adam at warski dot org)
*/
@Entity
@Audited
public class IndexedListJoinColumnBidirectionalRefEdEntity {
@Id
@GeneratedValue
private Integer id;
private String data;
@Column(name = "indexed_index", insertable = false, updatable = false)
private Integer position;
@ManyToOne
@JoinColumn(name = "indexed_join_column", insertable = false, updatable = false)
private IndexedListJoinColumnBidirectionalRefIngEntity owner;
public IndexedListJoinColumnBidirectionalRefEdEntity() { }
public IndexedListJoinColumnBidirectionalRefEdEntity(Integer id, String data, IndexedListJoinColumnBidirectionalRefIngEntity owner) {
this.id = id;
this.data = data;
this.owner = owner;
}
public IndexedListJoinColumnBidirectionalRefEdEntity(String data, IndexedListJoinColumnBidirectionalRefIngEntity owner) {
this.data = data;
this.owner = owner;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public IndexedListJoinColumnBidirectionalRefIngEntity getOwner() {
return owner;
}
public void setOwner(IndexedListJoinColumnBidirectionalRefIngEntity owner) {
this.owner = owner;
}
public Integer getPosition() {
return position;
}
public void setPosition(Integer position) {
this.position = position;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof IndexedListJoinColumnBidirectionalRefEdEntity)) return false;
IndexedListJoinColumnBidirectionalRefEdEntity that = (IndexedListJoinColumnBidirectionalRefEdEntity) o;
if (data != null ? !data.equals(that.data) : that.data != null) return false;
//noinspection RedundantIfStatement
if (id != null ? !id.equals(that.id) : that.id != null) return false;
return true;
}
public int hashCode() {
int result;
result = (id != null ? id.hashCode() : 0);
result = 31 * result + (data != null ? data.hashCode() : 0);
return result;
}
public String toString() {
return "IndexedListJoinColumnBidirectionalRefEdEntity(id = " + id + ", data = " + data + ")";
}
}

View File

@ -0,0 +1,92 @@
package org.hibernate.envers.test.entities.onetomany.detached;
import org.hibernate.envers.Audited;
import org.hibernate.envers.AuditMappedBy;
import org.hibernate.annotations.IndexColumn;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
/**
* Entity for {@link org.hibernate.envers.test.integration.onetomany.detached.IndexedJoinColumnBidirectionalList} test.
* Owning side of the relation.
* @author Adam Warski (adam at warski dot org)
*/
@Entity
@Audited
public class IndexedListJoinColumnBidirectionalRefIngEntity {
@Id
@GeneratedValue
private Integer id;
private String data;
@OneToMany
@JoinColumn(name = "indexed_join_column")
@IndexColumn(name = "indexed_index")
@AuditMappedBy(mappedBy = "owner", positionMappedBy = "position")
private List<IndexedListJoinColumnBidirectionalRefEdEntity> references;
public IndexedListJoinColumnBidirectionalRefIngEntity() { }
public IndexedListJoinColumnBidirectionalRefIngEntity(Integer id, String data, IndexedListJoinColumnBidirectionalRefEdEntity... references) {
this.id = id;
this.data = data;
this.references = new ArrayList<IndexedListJoinColumnBidirectionalRefEdEntity>();
this.references.addAll(Arrays.asList(references));
}
public IndexedListJoinColumnBidirectionalRefIngEntity(String data, IndexedListJoinColumnBidirectionalRefEdEntity... references) {
this(null, data, references);
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public List<IndexedListJoinColumnBidirectionalRefEdEntity> getReferences() {
return references;
}
public void setReferences(List<IndexedListJoinColumnBidirectionalRefEdEntity> references) {
this.references = references;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof IndexedListJoinColumnBidirectionalRefIngEntity)) return false;
IndexedListJoinColumnBidirectionalRefIngEntity that = (IndexedListJoinColumnBidirectionalRefIngEntity) o;
if (data != null ? !data.equals(that.data) : that.data != null) return false;
//noinspection RedundantIfStatement
if (id != null ? !id.equals(that.id) : that.id != null) return false;
return true;
}
public int hashCode() {
int result;
result = (id != null ? id.hashCode() : 0);
result = 31 * result + (data != null ? data.hashCode() : 0);
return result;
}
public String toString() {
return "IndexedListJoinColumnBidirectionalRefIngEntity(id = " + id + ", data = " + data + ")";
}
}

View File

@ -0,0 +1,250 @@
/*
* 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.onetomany.detached;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.envers.test.AbstractEntityTest;
import org.hibernate.envers.test.entities.onetomany.detached.IndexedListJoinColumnBidirectionalRefIngEntity;
import org.hibernate.envers.test.entities.onetomany.detached.IndexedListJoinColumnBidirectionalRefEdEntity;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import org.testng.annotations.Test;
import javax.persistence.EntityManager;
import java.util.Arrays;
/**
* Test for a "fake" bidirectional mapping where one side uses @OneToMany+@JoinColumn (and thus owns the relatin),
* and the other uses a @ManyToOne(insertable=false, updatable=false).
* @author Adam Warski (adam at warski dot org)
*/
public class IndexedJoinColumnBidirectionalList extends AbstractEntityTest {
private Integer ed1_id;
private Integer ed2_id;
private Integer ed3_id;
private Integer ing1_id;
private Integer ing2_id;
public void configure(Ejb3Configuration cfg) {
cfg.addAnnotatedClass(IndexedListJoinColumnBidirectionalRefIngEntity.class);
cfg.addAnnotatedClass(IndexedListJoinColumnBidirectionalRefEdEntity.class);
}
@Test(enabled = true)
public void createData() {
EntityManager em = getEntityManager();
IndexedListJoinColumnBidirectionalRefEdEntity ed1 = new IndexedListJoinColumnBidirectionalRefEdEntity("ed1", null);
IndexedListJoinColumnBidirectionalRefEdEntity ed2 = new IndexedListJoinColumnBidirectionalRefEdEntity("ed2", null);
IndexedListJoinColumnBidirectionalRefEdEntity ed3 = new IndexedListJoinColumnBidirectionalRefEdEntity("ed3", null);
IndexedListJoinColumnBidirectionalRefIngEntity ing1 = new IndexedListJoinColumnBidirectionalRefIngEntity("coll1", ed1, ed2, ed3);
IndexedListJoinColumnBidirectionalRefIngEntity ing2 = new IndexedListJoinColumnBidirectionalRefIngEntity("coll1");
// Revision 1 (ing1: ed1, ed2, ed3)
em.getTransaction().begin();
em.persist(ed1);
em.persist(ed2);
em.persist(ed3);
em.persist(ing1);
em.persist(ing2);
em.getTransaction().commit();
// Revision 2 (ing1: ed1, ed3, ing2: ed2)
em.getTransaction().begin();
ing1 = em.find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing1.getId());
ing2 = em.find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing2.getId());
ed2 = em.find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed2.getId());
ing1.getReferences().remove(ed2);
ing2.getReferences().add(ed2);
em.getTransaction().commit();
em.clear();
// Revision 3 (ing1: ed3, ed1, ing2: ed2)
em.getTransaction().begin();
ing1 = em.find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing1.getId());
ing2 = em.find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing2.getId());
ed1 = em.find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed1.getId());
ed2 = em.find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed2.getId());
ed3 = em.find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed3.getId());
ing1.getReferences().remove(ed3);
ing1.getReferences().add(0, ed3);
em.getTransaction().commit();
em.clear();
// Revision 4 (ing1: ed2, ed3, ed1)
em.getTransaction().begin();
ing1 = em.find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing1.getId());
ing2 = em.find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing2.getId());
ed1 = em.find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed1.getId());
ed2 = em.find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed2.getId());
ed3 = em.find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed3.getId());
ing2.getReferences().remove(ed2);
ing1.getReferences().add(0, ed2);
em.getTransaction().commit();
em.clear();
//
ing1_id = ing1.getId();
ing2_id = ing2.getId();
ed1_id = ed1.getId();
ed2_id = ed2.getId();
ed3_id = ed3.getId();
}
@Test(enabled = true, dependsOnMethods = "createData")
public void testRevisionsCounts() {
assertEquals(Arrays.asList(1, 2, 3, 4), getAuditReader().getRevisions(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing1_id));
assertEquals(Arrays.asList(1, 2, 4), getAuditReader().getRevisions(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing2_id));
assertEquals(Arrays.asList(1, 3, 4), getAuditReader().getRevisions(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed1_id));
assertEquals(Arrays.asList(1, 2, 4), getAuditReader().getRevisions(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed2_id));
assertEquals(Arrays.asList(1, 2, 3, 4), getAuditReader().getRevisions(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed3_id));
}
@Test(enabled = true, dependsOnMethods = "createData")
public void testHistoryOfIng1() {
IndexedListJoinColumnBidirectionalRefEdEntity ed1 = getEntityManager().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed1_id);
IndexedListJoinColumnBidirectionalRefEdEntity ed2 = getEntityManager().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed2_id);
IndexedListJoinColumnBidirectionalRefEdEntity ed3 = getEntityManager().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed3_id);
IndexedListJoinColumnBidirectionalRefIngEntity rev1 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing1_id, 1);
IndexedListJoinColumnBidirectionalRefIngEntity rev2 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing1_id, 2);
IndexedListJoinColumnBidirectionalRefIngEntity rev3 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing1_id, 3);
IndexedListJoinColumnBidirectionalRefIngEntity rev4 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing1_id, 4);
assertEquals(rev1.getReferences().size(), 3);
assertEquals(rev1.getReferences().get(0), ed1);
assertEquals(rev1.getReferences().get(1), ed2);
assertEquals(rev1.getReferences().get(2), ed3);
assertEquals(rev2.getReferences().size(), 2);
assertEquals(rev2.getReferences().get(0), ed1);
assertEquals(rev2.getReferences().get(1), ed3);
assertEquals(rev3.getReferences().size(), 2);
assertEquals(rev3.getReferences().get(0), ed3);
assertEquals(rev3.getReferences().get(1), ed1);
assertEquals(rev4.getReferences().size(), 3);
assertEquals(rev4.getReferences().get(0), ed2);
assertEquals(rev4.getReferences().get(1), ed3);
assertEquals(rev4.getReferences().get(2), ed1);
}
@Test(enabled = true, dependsOnMethods = "createData")
public void testHistoryOfIng2() {
IndexedListJoinColumnBidirectionalRefEdEntity ed2 = getEntityManager().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed2_id);
IndexedListJoinColumnBidirectionalRefIngEntity rev1 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing2_id, 1);
IndexedListJoinColumnBidirectionalRefIngEntity rev2 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing2_id, 2);
IndexedListJoinColumnBidirectionalRefIngEntity rev3 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing2_id, 3);
IndexedListJoinColumnBidirectionalRefIngEntity rev4 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing2_id, 4);
assertEquals(rev1.getReferences().size(), 0);
assertEquals(rev2.getReferences().size(), 1);
assertEquals(rev2.getReferences().get(0), ed2);
assertEquals(rev3.getReferences().size(), 1);
assertEquals(rev3.getReferences().get(0), ed2);
assertEquals(rev4.getReferences().size(), 0);
}
@Test(enabled = true, dependsOnMethods = "createData")
public void testHistoryOfEd1() {
IndexedListJoinColumnBidirectionalRefIngEntity ing1 = getEntityManager().find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing1_id);
IndexedListJoinColumnBidirectionalRefEdEntity rev1 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed1_id, 1);
IndexedListJoinColumnBidirectionalRefEdEntity rev2 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed1_id, 2);
IndexedListJoinColumnBidirectionalRefEdEntity rev3 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed1_id, 3);
IndexedListJoinColumnBidirectionalRefEdEntity rev4 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed1_id, 4);
assertTrue(rev1.getOwner().equals(ing1));
assertTrue(rev2.getOwner().equals(ing1));
assertTrue(rev3.getOwner().equals(ing1));
assertTrue(rev4.getOwner().equals(ing1));
assertEquals(rev1.getPosition(), new Integer(0));
assertEquals(rev2.getPosition(), new Integer(0));
assertEquals(rev3.getPosition(), new Integer(1));
assertEquals(rev4.getPosition(), new Integer(2));
}
@Test(enabled = true, dependsOnMethods = "createData")
public void testHistoryOfEd2() {
IndexedListJoinColumnBidirectionalRefIngEntity ing1 = getEntityManager().find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing1_id);
IndexedListJoinColumnBidirectionalRefIngEntity ing2 = getEntityManager().find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing2_id);
IndexedListJoinColumnBidirectionalRefEdEntity rev1 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed2_id, 1);
IndexedListJoinColumnBidirectionalRefEdEntity rev2 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed2_id, 2);
IndexedListJoinColumnBidirectionalRefEdEntity rev3 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed2_id, 3);
IndexedListJoinColumnBidirectionalRefEdEntity rev4 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed2_id, 4);
assertTrue(rev1.getOwner().equals(ing1));
assertTrue(rev2.getOwner().equals(ing2));
assertTrue(rev3.getOwner().equals(ing2));
assertTrue(rev4.getOwner().equals(ing1));
assertEquals(rev1.getPosition(), new Integer(1));
assertEquals(rev2.getPosition(), new Integer(0));
assertEquals(rev3.getPosition(), new Integer(0));
assertEquals(rev4.getPosition(), new Integer(0));
}
@Test(enabled = true, dependsOnMethods = "createData")
public void testHistoryOfEd3() {
IndexedListJoinColumnBidirectionalRefIngEntity ing1 = getEntityManager().find(IndexedListJoinColumnBidirectionalRefIngEntity.class, ing1_id);
IndexedListJoinColumnBidirectionalRefEdEntity rev1 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed3_id, 1);
IndexedListJoinColumnBidirectionalRefEdEntity rev2 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed3_id, 2);
IndexedListJoinColumnBidirectionalRefEdEntity rev3 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed3_id, 3);
IndexedListJoinColumnBidirectionalRefEdEntity rev4 = getAuditReader().find(IndexedListJoinColumnBidirectionalRefEdEntity.class, ed3_id, 4);
assertTrue(rev1.getOwner().equals(ing1));
assertTrue(rev2.getOwner().equals(ing1));
assertTrue(rev3.getOwner().equals(ing1));
assertTrue(rev4.getOwner().equals(ing1));
assertEquals(rev1.getPosition(), new Integer(2));
assertEquals(rev2.getPosition(), new Integer(1));
assertEquals(rev3.getPosition(), new Integer(0));
assertEquals(rev4.getPosition(), new Integer(1));
}
}