diff --git a/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java b/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java index 01e8bcce21..e1d8711a1f 100644 --- a/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java +++ b/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java @@ -224,7 +224,7 @@ public final class CollectionMetadataGenerator { // The mapper will only be used to map from entity to map, so no need to provide other details // when constructing the PropertyData. new PropertyData(auditMappedBy, null, null, null), - referencedEntityName); + referencedEntityName, false); } else { fakeBidirectionalRelationMapper = null; } diff --git a/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java b/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java index 43272072c0..9af1de4b79 100644 --- a/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java +++ b/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java @@ -70,7 +70,7 @@ public final class ToOneRelationMetadataGenerator { // Storing information about this relation mainGenerator.getEntitiesConfigurations().get(entityName).addToOneRelation( - propertyAuditingData.getName(), referencedEntityName, relMapper); + propertyAuditingData.getName(), referencedEntityName, relMapper, insertable); // If the property isn't insertable, checking if this is not a "fake" bidirectional many-to-one relationship, // that is, when the one side owns the relation (and is a collection), and the many side is non insertable. @@ -79,6 +79,7 @@ public final class ToOneRelationMetadataGenerator { // 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 // proper data. + boolean nonInsertableFake = false; if (!insertable) { ClassAuditingData referencedAuditingData = mainGenerator.getClassesAuditingData().getClassAuditingData(referencedEntityName); @@ -91,6 +92,7 @@ public final class ToOneRelationMetadataGenerator { referencedEntityName + " entity."); insertable = true; + nonInsertableFake = true; break; } } @@ -106,7 +108,7 @@ public final class ToOneRelationMetadataGenerator { // Adding mapper for the id PropertyData propertyData = propertyAuditingData.getPropertyData(); - mapper.addComposite(propertyData, new ToOneIdMapper(relMapper, propertyData, referencedEntityName)); + mapper.addComposite(propertyData, new ToOneIdMapper(relMapper, propertyData, referencedEntityName, nonInsertableFake)); } @SuppressWarnings({"unchecked"}) diff --git a/envers/src/main/java/org/hibernate/envers/entities/EntityConfiguration.java b/envers/src/main/java/org/hibernate/envers/entities/EntityConfiguration.java index 6279cc0778..8fadba9fd9 100644 --- a/envers/src/main/java/org/hibernate/envers/entities/EntityConfiguration.java +++ b/envers/src/main/java/org/hibernate/envers/entities/EntityConfiguration.java @@ -51,31 +51,31 @@ public class EntityConfiguration { this.relations = new HashMap(); } - public void addToOneRelation(String fromPropertyName, String toEntityName, IdMapper idMapper) { + public void addToOneRelation(String fromPropertyName, String toEntityName, IdMapper idMapper, boolean insertable) { relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_ONE, - toEntityName, null, idMapper, null)); + toEntityName, null, idMapper, null, insertable)); } public void addToOneNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName, IdMapper idMapper) { relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_ONE_NOT_OWNING, - toEntityName, mappedByPropertyName, idMapper, null)); + toEntityName, mappedByPropertyName, idMapper, null, true)); } public void addToManyNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName, IdMapper idMapper, PropertyMapper fakeBidirectionalRelationMapper) { relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_NOT_OWNING, - toEntityName, mappedByPropertyName, idMapper, fakeBidirectionalRelationMapper)); + toEntityName, mappedByPropertyName, idMapper, fakeBidirectionalRelationMapper, true)); } public void addToManyMiddleRelation(String fromPropertyName, String toEntityName) { relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_MIDDLE, - toEntityName, null, null, null)); + toEntityName, null, null, null, true)); } public void addToManyMiddleNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName) { relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_MIDDLE_NOT_OWNING, - toEntityName, mappedByPropertyName, null, null)); + toEntityName, mappedByPropertyName, null, null, true)); } public boolean isRelation(String propertyName) { diff --git a/envers/src/main/java/org/hibernate/envers/entities/RelationDescription.java b/envers/src/main/java/org/hibernate/envers/entities/RelationDescription.java index 6329b1ee9d..467e324a61 100644 --- a/envers/src/main/java/org/hibernate/envers/entities/RelationDescription.java +++ b/envers/src/main/java/org/hibernate/envers/entities/RelationDescription.java @@ -36,17 +36,19 @@ public class RelationDescription { private final String mappedByPropertyName; private final IdMapper idMapper; private final PropertyMapper fakeBidirectionalRelationMapper; + private final boolean insertable; private boolean bidirectional; public RelationDescription(String fromPropertyName, RelationType relationType, String toEntityName, String mappedByPropertyName, IdMapper idMapper, - PropertyMapper fakeBidirectionalRelationMapper) { + PropertyMapper fakeBidirectionalRelationMapper, boolean insertable) { this.fromPropertyName = fromPropertyName; this.relationType = relationType; this.toEntityName = toEntityName; this.mappedByPropertyName = mappedByPropertyName; this.idMapper = idMapper; this.fakeBidirectionalRelationMapper = fakeBidirectionalRelationMapper; + this.insertable = insertable; this.bidirectional = false; } @@ -75,6 +77,10 @@ public class RelationDescription { return fakeBidirectionalRelationMapper; } + public boolean isInsertable() { + return insertable; + } + public boolean isBidirectional() { return bidirectional; } diff --git a/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java b/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java index cf3dd65ec3..d6fd5df14e 100644 --- a/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java +++ b/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java @@ -49,20 +49,26 @@ public class ToOneIdMapper implements PropertyMapper { private final IdMapper delegate; private final PropertyData propertyData; private final String referencedEntityName; + private final boolean nonInsertableFake; - public ToOneIdMapper(IdMapper delegate, PropertyData propertyData, String referencedEntityName) { + public ToOneIdMapper(IdMapper delegate, PropertyData propertyData, String referencedEntityName, boolean nonInsertableFake) { this.delegate = delegate; this.propertyData = propertyData; this.referencedEntityName = referencedEntityName; + this.nonInsertableFake = nonInsertableFake; } public boolean mapToMapFromEntity(SessionImplementor session, Map data, Object newObj, Object oldObj) { HashMap newData = new HashMap(); data.put(propertyData.getName(), newData); - delegate.mapToMapFromEntity(newData, newObj); + // If this property is originally non-insertable, but made insertable because it is in a many-to-one "fake" + // bi-directional relation, we always store the "old", unchaged data, to prevent storing changes made + // to this field. It is the responsibility of the collection to properly update it if it really changed. + delegate.mapToMapFromEntity(newData, nonInsertableFake ? oldObj : newObj); - return !Tools.entitiesEqual(session, newObj, oldObj); + //noinspection SimplifiableConditionalExpression + return nonInsertableFake ? false : !Tools.entitiesEqual(session, newObj, oldObj); } public void mapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object primaryKey, diff --git a/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java b/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java index ee70e5f938..a813bda19e 100644 --- a/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java +++ b/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java @@ -84,7 +84,8 @@ public class AuditEventListener implements PostInsertEventListener, PostUpdateEv for (int i=0; i