HHH-9834 - Fix failure when deleting and inserting values with the same Map key.

This commit is contained in:
Chris Cranford 2016-12-22 16:07:51 -05:00
parent 36e75f4a4d
commit 1d41a3f761
2 changed files with 58 additions and 33 deletions

View File

@ -449,7 +449,7 @@ public final class CollectionMetadataGenerator {
mainGenerator.getAuditStrategy(),
referencingIdData,
auditMiddleEntityName,
isEmbeddableElementType()
isRevisionTypeInId()
);
// Adding the XML mapping for the referencing entity, if the relation isn't inverse.
@ -471,7 +471,7 @@ public final class CollectionMetadataGenerator {
queryGeneratorBuilder,
referencedPrefix,
propertyAuditingData.getJoinTable().inverseJoinColumns(),
isCollectionElementKeyProperty()
!isLobMapElementType()
);
// ******
@ -723,6 +723,7 @@ public final class CollectionMetadataGenerator {
MiddleComponentData indexComponentData) {
final Type type = propertyValue.getType();
final boolean embeddableElementType = isEmbeddableElementType();
final boolean lobMapElementType = isLobMapElementType();
if ( type instanceof SortedSetType ) {
currentMapper.addComposite(
propertyAuditingData.getPropertyData(),
@ -761,7 +762,7 @@ public final class CollectionMetadataGenerator {
elementComponentData,
indexComponentData,
propertyValue.getComparator(),
embeddableElementType
embeddableElementType || lobMapElementType
)
);
}
@ -775,7 +776,7 @@ public final class CollectionMetadataGenerator {
MapProxy.class,
elementComponentData,
indexComponentData,
embeddableElementType
embeddableElementType || lobMapElementType
)
);
}
@ -854,9 +855,9 @@ public final class CollectionMetadataGenerator {
// Adding the revision type property to the entity xml.
mainGenerator.addRevisionType(
isEmbeddableElementType() ? middleEntityXmlId : middleEntityXml,
isRevisionTypeInId() ? middleEntityXmlId : middleEntityXml,
middleEntityXml,
isEmbeddableElementType()
isRevisionTypeInId()
);
// All other properties should also be part of the primary key of the middle entity.
@ -1023,20 +1024,27 @@ public final class CollectionMetadataGenerator {
}
/**
* Checks whether the collection element should participate in the primary key association. This
* is only applicable for non-component, non-associative collection element types.
* Returns whether the revision type column part of the collection table's primary key.
*
* @return {@code true} if should be part of the primary key, {@code false} if not.
* @return {@code true} if the revision type should be part of the primary key, otherwise {@code false}.
*/
private boolean isCollectionElementKeyProperty() {
// When List or Set are used, the element will always be part of the primary key.
// When using a Map, the element isn't required as the Map Key will suffice.
private boolean isRevisionTypeInId() {
return isEmbeddableElementType() || isLobMapElementType();
}
/**
* Returns whether the collection is a map-type and that the map element is defined as a Clob/NClob type.
*
* @return {@code true} if the element is a Clob/NClob type, otherwise {@code false}.
*/
private boolean isLobMapElementType() {
if ( propertyValue instanceof org.hibernate.mapping.Map ) {
final Type type = propertyValue.getElement().getType();
// we're only interested in basic types
if ( !type.isComponentType() && !type.isAssociationType() ) {
return !( type instanceof MaterializedClobType || type instanceof MaterializedNClobType );
return ( type instanceof MaterializedClobType ) || ( type instanceof MaterializedNClobType );
}
}
return true;
return false;
}
}

View File

@ -36,6 +36,9 @@ import org.hibernate.property.access.spi.Getter;
import org.hibernate.sql.Update;
import org.hibernate.type.CollectionType;
import org.hibernate.type.ComponentType;
import org.hibernate.type.MapType;
import org.hibernate.type.MaterializedClobType;
import org.hibernate.type.MaterializedNClobType;
import org.hibernate.type.Type;
import static org.hibernate.envers.internal.entities.mapper.relation.query.QueryConstants.MIDDLE_ENTITY_ALIAS;
@ -260,25 +263,8 @@ public class ValidityAuditStrategy implements AuditStrategy {
}
}
final SessionFactoryImplementor sessionFactory = ( (SessionImplementor) session ).getFactory();
final Type propertyType = sessionFactory.getMetamodel().entityPersister( 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.
// todo: is it necessary that non-primary key attributes be compared?
for ( Map.Entry<String, Object> dataEntry : persistentCollectionChangeData.getData().entrySet() ) {
if ( !originalIdPropName.equals( dataEntry.getKey() ) ) {
if ( dataEntry.getValue() != null ) {
qb.getRootParameters().addWhereWithParam( dataEntry.getKey(), true, "=", dataEntry.getValue() );
}
else {
qb.getRootParameters().addNullRestriction( dataEntry.getKey(), true );
}
}
}
}
if ( isNonIdentifierWhereConditionsRequired( entityName, propertyName, (SessionImplementor) session ) ) {
addNonIdentifierWhereConditions( qb, persistentCollectionChangeData.getData(), originalIdPropName );
}
addEndRevisionNullRestriction( auditEntitiesConfiguration, qb.getRootParameters() );
@ -299,6 +285,37 @@ public class ValidityAuditStrategy implements AuditStrategy {
sessionCacheCleaner.scheduleAuditDataRemoval( session, persistentCollectionChangeData.getData() );
}
private boolean isNonIdentifierWhereConditionsRequired(String entityName, String propertyName, SessionImplementor session) {
final Type propertyType = session.getSessionFactory().getMetamodel().entityPersister( entityName ).getPropertyType( propertyName );
if ( propertyType.isCollectionType() ) {
final CollectionType collectionType = (CollectionType) propertyType;
final Type collectionElementType = collectionType.getElementType( session.getSessionFactory() );
if ( collectionElementType instanceof ComponentType ) {
// required for Embeddables
return true;
}
else if ( collectionElementType instanceof MaterializedClobType || collectionElementType instanceof MaterializedNClobType ) {
// for Map<> using @Lob annotations
return collectionType instanceof MapType;
}
}
return false;
}
private void addNonIdentifierWhereConditions(QueryBuilder qb, Map<String, Object> data, String originalIdPropertyName) {
final Parameters parameters = qb.getRootParameters();
for ( Map.Entry<String, Object> entry : data.entrySet() ) {
if ( !originalIdPropertyName.equals( entry.getKey() ) ) {
if ( entry.getValue() != null ) {
parameters.addWhereWithParam( entry.getKey(), true, "=", entry.getValue() );
}
else {
parameters.addNullRestriction( entry.getKey(), true );
}
}
}
}
private void addEndRevisionNullRestriction(AuditEntitiesConfiguration auditEntitiesConfiguration, Parameters rootParameters) {
rootParameters.addWhere( auditEntitiesConfiguration.getRevisionEndFieldName(), true, "is", "null", false );
}