HHH-12607 - Support map-based collections without equals/hashcode implementations.
This commit is contained in:
parent
00a56a188b
commit
0b7c1e2fcb
|
@ -242,9 +242,12 @@ public final class CollectionMetadataGenerator {
|
|||
|
||||
// Creating common mapper data.
|
||||
final CommonCollectionMapperData commonCollectionMapperData = new CommonCollectionMapperData(
|
||||
mainGenerator.getVerEntCfg(), referencedEntityName,
|
||||
mainGenerator.getVerEntCfg(),
|
||||
referencedEntityName,
|
||||
propertyAuditingData.getPropertyData(),
|
||||
referencingIdData, queryGenerator
|
||||
referencingIdData,
|
||||
queryGenerator,
|
||||
propertyValue.getRole()
|
||||
);
|
||||
|
||||
PropertyMapper fakeBidirectionalRelationMapper;
|
||||
|
@ -490,7 +493,8 @@ public final class CollectionMetadataGenerator {
|
|||
auditMiddleEntityName,
|
||||
propertyAuditingData.getPropertyData(),
|
||||
referencingIdData,
|
||||
queryGenerator
|
||||
queryGenerator,
|
||||
propertyValue.getRole()
|
||||
);
|
||||
|
||||
// Checking the type of the collection and adding an appropriate mapper.
|
||||
|
|
|
@ -8,19 +8,15 @@ package org.hibernate.envers.internal.entities.mapper.relation;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.collection.internal.PersistentMap;
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.CollectionEntry;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
|
@ -52,7 +48,9 @@ public abstract class AbstractCollectionMapper<T> extends AbstractPropertyMapper
|
|||
|
||||
protected AbstractCollectionMapper(
|
||||
CommonCollectionMapperData commonCollectionMapperData,
|
||||
Class<? extends T> collectionClass, Class<? extends T> proxyClass, boolean ordinalInId,
|
||||
Class<? extends T> collectionClass,
|
||||
Class<? extends T> proxyClass,
|
||||
boolean ordinalInId,
|
||||
boolean revisionTypeInId) {
|
||||
this.commonCollectionMapperData = commonCollectionMapperData;
|
||||
this.collectionClass = collectionClass;
|
||||
|
@ -71,6 +69,8 @@ public abstract class AbstractCollectionMapper<T> extends AbstractPropertyMapper
|
|||
|
||||
protected abstract Collection getOldCollectionContent(Serializable oldCollection);
|
||||
|
||||
protected abstract Set<Object> buildCollectionChangeSet(Object eventCollection, Collection collection);
|
||||
|
||||
/**
|
||||
* Maps the changed collection element to the given map.
|
||||
*
|
||||
|
@ -100,9 +100,12 @@ public abstract class AbstractCollectionMapper<T> extends AbstractPropertyMapper
|
|||
return idMap;
|
||||
}
|
||||
|
||||
private void addCollectionChanges(
|
||||
SessionImplementor session, List<PersistentCollectionChangeData> collectionChanges,
|
||||
Set<Object> changed, RevisionType revisionType, Serializable id) {
|
||||
protected void addCollectionChanges(
|
||||
SessionImplementor session,
|
||||
List<PersistentCollectionChangeData> collectionChanges,
|
||||
Set<Object> changed,
|
||||
RevisionType revisionType,
|
||||
Serializable id) {
|
||||
int ordinal = 0;
|
||||
|
||||
for ( Object changedObj : changed ) {
|
||||
|
@ -134,39 +137,13 @@ public abstract class AbstractCollectionMapper<T> extends AbstractPropertyMapper
|
|||
SessionImplementor session,
|
||||
String referencingPropertyName,
|
||||
PersistentCollection newColl,
|
||||
Serializable oldColl, Serializable id) {
|
||||
if ( !commonCollectionMapperData.getCollectionReferencingPropertyData().getName().equals(
|
||||
referencingPropertyName
|
||||
) ) {
|
||||
Serializable oldColl,
|
||||
Serializable id) {
|
||||
final PropertyData propertyData = commonCollectionMapperData.getCollectionReferencingPropertyData();
|
||||
if ( !propertyData.getName().equals( referencingPropertyName ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// HHH-11063
|
||||
final CollectionEntry collectionEntry = session.getPersistenceContext().getCollectionEntry( newColl );
|
||||
if ( collectionEntry != null ) {
|
||||
// This next block delegates only to the persiter-based collection change code if
|
||||
// the following are true:
|
||||
// 1. New collection is not a PersistentMap.
|
||||
// 2. The collection has a persister.
|
||||
// 3. The collection is not indexed, e.g. @IndexColumn
|
||||
//
|
||||
// In the case of 1 and 3, the collection is transformed into a set of Pair<> elements where the
|
||||
// pair's left element is either the map key or the index. In these cases, the key/index do
|
||||
// affect the change code; hence why they're skipped here and handled at the end.
|
||||
//
|
||||
// For all others, the persister based method uses the collection's ElementType#isSame to calculate
|
||||
// equality between the newColl and oldColl. This enforces the same equality check that core uses
|
||||
// for element types such as @Entity in cases where the hash code does not use the id field but has
|
||||
// the same value in both collections. Using #isSame, these will be seen as differing elements and
|
||||
// changes to the collection will be returned.
|
||||
if ( !( newColl instanceof PersistentMap ) ) {
|
||||
final CollectionPersister collectionPersister = collectionEntry.getCurrentPersister();
|
||||
if ( collectionPersister != null && !collectionPersister.hasIndex() ) {
|
||||
return mapCollectionChanges( session, newColl, oldColl, id, collectionPersister );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mapCollectionChanges( session, newColl, oldColl, id );
|
||||
}
|
||||
|
||||
|
@ -247,6 +224,52 @@ public abstract class AbstractCollectionMapper<T> extends AbstractPropertyMapper
|
|||
Number revision,
|
||||
boolean removed);
|
||||
|
||||
protected CollectionPersister resolveCollectionPersister(
|
||||
SessionImplementor session,
|
||||
PersistentCollection collection) {
|
||||
// First attempt to resolve the persister from the collection entry
|
||||
if ( collection != null ) {
|
||||
CollectionEntry collectionEntry = session.getPersistenceContext().getCollectionEntry( collection );
|
||||
if ( collectionEntry != null ) {
|
||||
CollectionPersister collectionPersister = collectionEntry.getCurrentPersister();
|
||||
if ( collectionPersister != null ) {
|
||||
return collectionPersister;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to resolving the persister from the collection role
|
||||
final CollectionPersister collectionPersister = session.getFactory()
|
||||
.getMetamodel()
|
||||
.collectionPersister( commonCollectionMapperData.getRole() );
|
||||
|
||||
if ( collectionPersister == null ) {
|
||||
throw new AuditException(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"Failed to locate CollectionPersister for collection [%s]",
|
||||
commonCollectionMapperData.getRole()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return collectionPersister;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the old collection element and new collection element are the same.
|
||||
* By default, this delegates to the collection persister's {@link CollectionPersister#getElementType()}.
|
||||
*
|
||||
* @param collectionPersister The collection persister.
|
||||
* @param oldObject The collection element from the old persistent collection.
|
||||
* @param newObject The collection element from the new persistent collection.
|
||||
*
|
||||
* @return true if the two objects are the same, false otherwise.
|
||||
*/
|
||||
protected boolean isSame(CollectionPersister collectionPersister, Object oldObject, Object newObject) {
|
||||
return collectionPersister.getElementType().isSame( oldObject, newObject );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapToEntityFromMap(
|
||||
final EnversService enversService,
|
||||
|
@ -256,26 +279,29 @@ public abstract class AbstractCollectionMapper<T> extends AbstractPropertyMapper
|
|||
final AuditReaderImplementor versionsReader,
|
||||
final Number revision) {
|
||||
final String revisionTypePropertyName = enversService.getAuditEntitiesConfiguration().getRevisionTypePropName();
|
||||
|
||||
// construct the collection proxy
|
||||
final Object collectionProxy;
|
||||
try {
|
||||
collectionProxy = proxyConstructor.newInstance(
|
||||
getInitializor(
|
||||
enversService,
|
||||
versionsReader,
|
||||
primaryKey,
|
||||
revision,
|
||||
RevisionType.DEL.equals( data.get( revisionTypePropertyName ) )
|
||||
)
|
||||
);
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
throw new AuditException( "Failed to construct collection proxy", e );
|
||||
}
|
||||
|
||||
final PropertyData collectionPropertyData = commonCollectionMapperData.getCollectionReferencingPropertyData();
|
||||
|
||||
if ( isDynamicComponentMap() ) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<String, Object> map = (Map<String, Object>) obj;
|
||||
try {
|
||||
map.put(
|
||||
commonCollectionMapperData.getCollectionReferencingPropertyData().getBeanName(),
|
||||
proxyConstructor.newInstance(
|
||||
getInitializor(
|
||||
enversService,
|
||||
versionsReader,
|
||||
primaryKey,
|
||||
revision,
|
||||
RevisionType.DEL.equals( data.get( revisionTypePropertyName ) )
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
throw new AuditException( e );
|
||||
}
|
||||
map.put( collectionPropertyData.getBeanName(), collectionProxy );
|
||||
}
|
||||
else {
|
||||
AccessController.doPrivileged(
|
||||
|
@ -284,34 +310,11 @@ public abstract class AbstractCollectionMapper<T> extends AbstractPropertyMapper
|
|||
public Object run() {
|
||||
final Setter setter = ReflectionTools.getSetter(
|
||||
obj.getClass(),
|
||||
commonCollectionMapperData.getCollectionReferencingPropertyData(),
|
||||
collectionPropertyData,
|
||||
enversService.getServiceRegistry()
|
||||
);
|
||||
|
||||
try {
|
||||
setter.set(
|
||||
obj,
|
||||
proxyConstructor.newInstance(
|
||||
getInitializor(
|
||||
enversService,
|
||||
versionsReader,
|
||||
primaryKey,
|
||||
revision,
|
||||
RevisionType.DEL.equals( data.get( revisionTypePropertyName ) )
|
||||
)
|
||||
),
|
||||
null
|
||||
);
|
||||
}
|
||||
catch ( InstantiationException e ) {
|
||||
throw new AuditException( e );
|
||||
}
|
||||
catch ( IllegalAccessException e ) {
|
||||
throw new AuditException( e );
|
||||
}
|
||||
catch ( InvocationTargetException e ) {
|
||||
throw new AuditException( e );
|
||||
}
|
||||
setter.set( obj, collectionProxy, null );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -329,95 +332,11 @@ public abstract class AbstractCollectionMapper<T> extends AbstractPropertyMapper
|
|||
* @param id The owning entity identifier.
|
||||
* @return the persistent collection changes.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<PersistentCollectionChangeData> mapCollectionChanges(
|
||||
protected abstract List<PersistentCollectionChangeData> mapCollectionChanges(
|
||||
SessionImplementor session,
|
||||
PersistentCollection newColl,
|
||||
Serializable oldColl,
|
||||
Serializable id) {
|
||||
final List<PersistentCollectionChangeData> collectionChanges = new ArrayList<PersistentCollectionChangeData>();
|
||||
|
||||
// Comparing new and old collection content.
|
||||
final Collection newCollection = getNewCollectionContent( newColl );
|
||||
final Collection oldCollection = getOldCollectionContent( oldColl );
|
||||
|
||||
final Set<Object> added = buildCollectionChangeSet( newColl, newCollection );
|
||||
// Re-hashing the old collection as the hash codes of the elements there may have changed, and the
|
||||
// removeAll in AbstractSet has an implementation that is hashcode-change sensitive (as opposed to addAll).
|
||||
if ( oldColl != null ) {
|
||||
added.removeAll( new HashSet( oldCollection ) );
|
||||
}
|
||||
addCollectionChanges( session, collectionChanges, added, RevisionType.ADD, id );
|
||||
|
||||
final Set<Object> deleted = buildCollectionChangeSet( oldColl, oldCollection );
|
||||
// The same as above - re-hashing new collection.
|
||||
if ( newColl != null ) {
|
||||
deleted.removeAll( new HashSet( newCollection ) );
|
||||
}
|
||||
addCollectionChanges( session, collectionChanges, deleted, RevisionType.DEL, id );
|
||||
|
||||
return collectionChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map collection changes using the collection element type equality functionality.
|
||||
*
|
||||
* @param session The session.
|
||||
* @param newColl The new persistent collection.
|
||||
* @param oldColl The old collection.
|
||||
* @param id The owning entity identifier.
|
||||
* @param collectionPersister The collection persister.
|
||||
* @return the persistent collection changes.
|
||||
*/
|
||||
private List<PersistentCollectionChangeData> mapCollectionChanges(
|
||||
SessionImplementor session,
|
||||
PersistentCollection newColl,
|
||||
Serializable oldColl,
|
||||
Serializable id,
|
||||
CollectionPersister collectionPersister) {
|
||||
|
||||
final List<PersistentCollectionChangeData> collectionChanges = new ArrayList<PersistentCollectionChangeData>();
|
||||
|
||||
// Comparing new and old collection content.
|
||||
final Collection newCollection = getNewCollectionContent( newColl );
|
||||
final Collection oldCollection = getOldCollectionContent( oldColl );
|
||||
|
||||
// take the new collection and remove any that exist in the old collection.
|
||||
// take the resulting Set<> and generate ADD changes.
|
||||
final Set<Object> added = buildCollectionChangeSet( newColl, newCollection );
|
||||
if ( oldColl != null && collectionPersister != null ) {
|
||||
for ( Object object : oldCollection ) {
|
||||
for ( Iterator addedIt = added.iterator(); addedIt.hasNext(); ) {
|
||||
Object object2 = addedIt.next();
|
||||
if ( collectionPersister.getElementType().isSame( object, object2 ) ) {
|
||||
addedIt.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
addCollectionChanges( session, collectionChanges, added, RevisionType.ADD, id );
|
||||
|
||||
// take the old collection and remove any that exist in the new collection.
|
||||
// take the resulting Set<> and generate DEL changes.
|
||||
final Set<Object> deleted = buildCollectionChangeSet( oldColl, oldCollection );
|
||||
if ( newColl != null && collectionPersister != null ) {
|
||||
for ( Object object : newCollection ) {
|
||||
for ( Iterator deletedIt = deleted.iterator(); deletedIt.hasNext(); ) {
|
||||
Object object2 = deletedIt.next();
|
||||
if ( collectionPersister.getElementType().isSame( object, object2 ) ) {
|
||||
deletedIt.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
addCollectionChanges( session, collectionChanges, deleted, RevisionType.DEL, id );
|
||||
|
||||
return collectionChanges;
|
||||
}
|
||||
|
||||
protected abstract Set<Object> buildCollectionChangeSet(Object eventCollection, Collection collection);
|
||||
Serializable id);
|
||||
|
||||
@Override
|
||||
public boolean hasPropertiesWithModifiedFlag() {
|
||||
|
|
|
@ -7,18 +7,24 @@
|
|||
package org.hibernate.envers.internal.entities.mapper.relation;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.envers.RevisionType;
|
||||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.internal.entities.mapper.PersistentCollectionChangeData;
|
||||
import org.hibernate.envers.internal.entities.mapper.PropertyMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.lazy.initializor.BasicCollectionInitializor;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.lazy.initializor.Initializor;
|
||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
|
||||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
|
@ -96,4 +102,51 @@ public class BasicCollectionMapper<T extends Collection> extends AbstractCollect
|
|||
}
|
||||
return changeSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<PersistentCollectionChangeData> mapCollectionChanges(
|
||||
SessionImplementor session,
|
||||
PersistentCollection newColl,
|
||||
Serializable oldColl,
|
||||
Serializable id) {
|
||||
|
||||
final List<PersistentCollectionChangeData> collectionChanges = new ArrayList<>();
|
||||
|
||||
final CollectionPersister collectionPersister = resolveCollectionPersister( session, newColl );
|
||||
|
||||
// Comparing new and old collection content.
|
||||
final Collection newCollection = getNewCollectionContent( newColl );
|
||||
final Collection oldCollection = getOldCollectionContent( oldColl );
|
||||
|
||||
final Set<Object> addedElements = buildCollectionChangeSet( newColl, newCollection );
|
||||
if ( oldColl != null ) {
|
||||
for ( Object oldEntry : oldCollection ) {
|
||||
for ( Iterator itor = addedElements.iterator(); itor.hasNext(); ) {
|
||||
Object newEntry = itor.next();
|
||||
if ( collectionPersister.getElementType().isSame( oldEntry, newEntry ) ) {
|
||||
itor.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Object> deleteElements = buildCollectionChangeSet( oldColl, oldCollection );
|
||||
if ( newColl != null ) {
|
||||
for ( Object newEntry : newCollection ) {
|
||||
for ( Iterator itor = deleteElements.iterator(); itor.hasNext(); ) {
|
||||
Object deletedEntry = itor.next();
|
||||
if ( collectionPersister.getElementType().isSame( deletedEntry, newEntry ) ) {
|
||||
itor.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addCollectionChanges( session, collectionChanges, addedElements, RevisionType.ADD, id );
|
||||
addCollectionChanges( session, collectionChanges, deleteElements, RevisionType.DEL, id );
|
||||
|
||||
return collectionChanges;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,16 +21,21 @@ public final class CommonCollectionMapperData {
|
|||
private final PropertyData collectionReferencingPropertyData;
|
||||
private final MiddleIdData referencingIdData;
|
||||
private final RelationQueryGenerator queryGenerator;
|
||||
private final String collectionRole;
|
||||
|
||||
public CommonCollectionMapperData(
|
||||
AuditEntitiesConfiguration verEntCfg, String versionsMiddleEntityName,
|
||||
PropertyData collectionReferencingPropertyData, MiddleIdData referencingIdData,
|
||||
RelationQueryGenerator queryGenerator) {
|
||||
AuditEntitiesConfiguration verEntCfg,
|
||||
String versionsMiddleEntityName,
|
||||
PropertyData collectionReferencingPropertyData,
|
||||
MiddleIdData referencingIdData,
|
||||
RelationQueryGenerator queryGenerator,
|
||||
String collectionRole) {
|
||||
this.verEntCfg = verEntCfg;
|
||||
this.versionsMiddleEntityName = versionsMiddleEntityName;
|
||||
this.collectionReferencingPropertyData = collectionReferencingPropertyData;
|
||||
this.referencingIdData = referencingIdData;
|
||||
this.queryGenerator = queryGenerator;
|
||||
this.collectionRole = collectionRole;
|
||||
}
|
||||
|
||||
public AuditEntitiesConfiguration getVerEntCfg() {
|
||||
|
@ -52,4 +57,8 @@ public final class CommonCollectionMapperData {
|
|||
public RelationQueryGenerator getQueryGenerator() {
|
||||
return queryGenerator;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return collectionRole;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,15 +7,19 @@
|
|||
package org.hibernate.envers.internal.entities.mapper.relation;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.envers.RevisionType;
|
||||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.internal.entities.mapper.PersistentCollectionChangeData;
|
||||
import org.hibernate.envers.internal.entities.mapper.PropertyMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.lazy.initializor.Initializor;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.lazy.initializor.ListCollectionInitializor;
|
||||
|
@ -23,6 +27,7 @@ import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.ListPro
|
|||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||
import org.hibernate.envers.internal.tools.Tools;
|
||||
import org.hibernate.envers.tools.Pair;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
|
||||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
|
@ -115,4 +120,56 @@ public final class ListCollectionMapper extends AbstractCollectionMapper<List> i
|
|||
}
|
||||
return changeSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<PersistentCollectionChangeData> mapCollectionChanges(
|
||||
SessionImplementor session,
|
||||
PersistentCollection newColl,
|
||||
Serializable oldColl,
|
||||
Serializable id) {
|
||||
final List<PersistentCollectionChangeData> collectionChanges = new ArrayList<>();
|
||||
|
||||
final CollectionPersister collectionPersister = resolveCollectionPersister( session, newColl );
|
||||
|
||||
// Comparing new and old collection content.
|
||||
final Collection newCollection = getNewCollectionContent( newColl );
|
||||
final Collection oldCollection = getOldCollectionContent( oldColl );
|
||||
|
||||
final Set<Object> addedElements = buildCollectionChangeSet( newColl, newCollection );
|
||||
if ( oldColl != null ) {
|
||||
for ( int i = 0; i < oldCollection.size(); ++i ) {
|
||||
Pair<Integer, ?> oldEntry = ((List<Pair<Integer, ?>>) oldCollection).get( i );
|
||||
for ( Iterator itor = addedElements.iterator(); itor.hasNext(); ) {
|
||||
Pair<Integer, ?> addedEntry = (Pair<Integer, ?>) itor.next();
|
||||
if ( oldEntry.getFirst().equals( addedEntry.getFirst() ) ) {
|
||||
if ( isSame( collectionPersister, oldEntry.getSecond(), addedEntry.getSecond() ) ) {
|
||||
itor.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Object> deleteElements = buildCollectionChangeSet( oldColl, oldCollection );
|
||||
if ( newColl != null ) {
|
||||
for ( int i = 0; i < newCollection.size(); ++i ) {
|
||||
Pair<Integer, ?> newEntry = ((List<Pair<Integer, ?>>) newCollection).get( i );
|
||||
for ( Iterator itor = deleteElements.iterator(); itor.hasNext(); ) {
|
||||
Pair<Integer, ?> deletedEntry = (Pair<Integer, ?>) itor.next();
|
||||
if ( newEntry.getFirst().equals( deletedEntry.getFirst() ) ) {
|
||||
if ( isSame( collectionPersister, deletedEntry.getSecond(), newEntry.getSecond() ) ) {
|
||||
itor.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addCollectionChanges( session, collectionChanges, addedElements, RevisionType.ADD, id );
|
||||
addCollectionChanges( session, collectionChanges, deleteElements, RevisionType.DEL, id );
|
||||
|
||||
return collectionChanges;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,18 +7,24 @@
|
|||
package org.hibernate.envers.internal.entities.mapper.relation;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.envers.RevisionType;
|
||||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.internal.entities.mapper.PersistentCollectionChangeData;
|
||||
import org.hibernate.envers.internal.entities.mapper.PropertyMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.lazy.initializor.Initializor;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.lazy.initializor.MapCollectionInitializor;
|
||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
|
||||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
|
@ -114,4 +120,65 @@ public class MapCollectionMapper<T extends Map> extends AbstractCollectionMapper
|
|||
}
|
||||
return changeSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSame(CollectionPersister collectionPersister, Object oldObject, Object newObject) {
|
||||
final Map.Entry oldEntry = Map.Entry.class.cast( oldObject );
|
||||
final Map.Entry newEntry = Map.Entry.class.cast( newObject );
|
||||
if ( collectionPersister.getKeyType().isSame( oldEntry.getKey(), newEntry.getKey() ) ) {
|
||||
if ( collectionPersister.getElementType().isSame( oldEntry.getValue(), newEntry.getValue() ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PersistentCollectionChangeData> mapCollectionChanges(
|
||||
SessionImplementor session,
|
||||
PersistentCollection newColl,
|
||||
Serializable oldColl,
|
||||
Serializable id) {
|
||||
final List<PersistentCollectionChangeData> collectionChanges = new ArrayList<>();
|
||||
final CollectionPersister collectionPersister = resolveCollectionPersister( session, newColl );
|
||||
|
||||
// Comparing new and old collection content
|
||||
final Collection newCollection = getNewCollectionContent( newColl );
|
||||
final Collection oldCollection = getOldCollectionContent( oldColl );
|
||||
|
||||
// take the new collection and remove any that exist in the old collection.
|
||||
// take the resulting Set<> and generate ADD changes
|
||||
final Set<Object> added = buildCollectionChangeSet( newColl, newCollection );
|
||||
if ( oldColl != null ) {
|
||||
for ( Object oldObject : oldCollection ) {
|
||||
for ( Iterator itor = added.iterator(); itor.hasNext(); ) {
|
||||
Object newObject = itor.next();
|
||||
if ( isSame( collectionPersister, oldObject, newObject ) ) {
|
||||
itor.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// take the old collection and remove any that exist in the new collection.
|
||||
// take the resulting Set<> and generate DEL changes.
|
||||
final Set<Object> deleted = buildCollectionChangeSet( oldColl, oldCollection );
|
||||
if ( newColl != null ) {
|
||||
for ( Object newObject : newCollection ) {
|
||||
for ( Iterator itor = deleted.iterator(); itor.hasNext(); ) {
|
||||
Object oldObject = itor.next();
|
||||
if ( isSame( collectionPersister, newObject, oldObject ) ) {
|
||||
itor.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addCollectionChanges( session, collectionChanges, added, RevisionType.ADD, id );
|
||||
addCollectionChanges( session, collectionChanges, deleted, RevisionType.DEL, id );
|
||||
|
||||
return collectionChanges;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue