HHH-9108 - Fix PropertyAccessException when auditing an Embeddable that contains an associative collection.
This commit is contained in:
parent
61ce4b86ff
commit
652f85644f
|
@ -14,10 +14,13 @@ import java.util.Map;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.envers.ModificationStore;
|
import org.hibernate.envers.ModificationStore;
|
||||||
import org.hibernate.envers.RelationTargetAuditMode;
|
import org.hibernate.envers.RelationTargetAuditMode;
|
||||||
|
import org.hibernate.envers.configuration.internal.metadata.reader.AuditedPropertiesHolder;
|
||||||
import org.hibernate.envers.configuration.internal.metadata.reader.ClassAuditingData;
|
import org.hibernate.envers.configuration.internal.metadata.reader.ClassAuditingData;
|
||||||
|
import org.hibernate.envers.configuration.internal.metadata.reader.ComponentAuditingData;
|
||||||
import org.hibernate.envers.configuration.internal.metadata.reader.PropertyAuditingData;
|
import org.hibernate.envers.configuration.internal.metadata.reader.PropertyAuditingData;
|
||||||
import org.hibernate.envers.internal.EnversMessageLogger;
|
import org.hibernate.envers.internal.EnversMessageLogger;
|
||||||
import org.hibernate.envers.internal.tools.MappingTools;
|
import org.hibernate.envers.internal.tools.MappingTools;
|
||||||
|
import org.hibernate.mapping.Component;
|
||||||
import org.hibernate.mapping.List;
|
import org.hibernate.mapping.List;
|
||||||
import org.hibernate.mapping.PersistentClass;
|
import org.hibernate.mapping.PersistentClass;
|
||||||
|
|
||||||
|
@ -78,31 +81,26 @@ public class ClassesAuditingData {
|
||||||
for ( Map.Entry<PersistentClass, ClassAuditingData> classAuditingDataEntry : persistentClassToAuditingData.entrySet() ) {
|
for ( Map.Entry<PersistentClass, ClassAuditingData> classAuditingDataEntry : persistentClassToAuditingData.entrySet() ) {
|
||||||
final PersistentClass pc = classAuditingDataEntry.getKey();
|
final PersistentClass pc = classAuditingDataEntry.getKey();
|
||||||
final ClassAuditingData classAuditingData = classAuditingDataEntry.getValue();
|
final ClassAuditingData classAuditingData = classAuditingDataEntry.getValue();
|
||||||
for ( String propertyName : classAuditingData.getPropertyNames() ) {
|
for ( String propertyName : classAuditingData.getNonSyntheticPropertyNames() ) {
|
||||||
updateCalculatedProperty( pc, classAuditingData, propertyName );
|
final Property property = pc.getProperty( propertyName );
|
||||||
|
updateCalculatedProperty( pc.getEntityName(), property, propertyName, classAuditingData );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateCalculatedProperty(
|
private void updateCalculatedProperty(String entityName, Property property, String propertyName, AuditedPropertiesHolder propertyHolder) {
|
||||||
PersistentClass pc,
|
final PropertyAuditingData propertyAuditingData = propertyHolder.getPropertyAuditingData( propertyName );
|
||||||
ClassAuditingData classAuditingData,
|
|
||||||
String propertyName) {
|
|
||||||
|
|
||||||
final PropertyAuditingData propertyAuditingData = classAuditingData.getPropertyAuditingData( propertyName );
|
|
||||||
|
|
||||||
final boolean isAuditMappedBy = propertyAuditingData.getAuditMappedBy() != null;
|
final boolean isAuditMappedBy = propertyAuditingData.getAuditMappedBy() != null;
|
||||||
final boolean isRelationMappedBy = propertyAuditingData.getRelationMappedBy() != null;
|
final boolean isRelationMappedBy = propertyAuditingData.getRelationMappedBy() != null;
|
||||||
|
|
||||||
|
// handle updating the property, if applicable.
|
||||||
if ( isAuditMappedBy || isRelationMappedBy ) {
|
if ( isAuditMappedBy || isRelationMappedBy ) {
|
||||||
final Property property = pc.getProperty( propertyName );
|
|
||||||
final String referencedEntityName = MappingTools.getReferencedEntityName( property.getValue() );
|
final String referencedEntityName = MappingTools.getReferencedEntityName( property.getValue() );
|
||||||
|
|
||||||
final ClassAuditingData referencedAuditData = entityNameToAuditingData.get( referencedEntityName );
|
final ClassAuditingData referencedAuditData = entityNameToAuditingData.get( referencedEntityName );
|
||||||
|
|
||||||
if ( isAuditMappedBy ) {
|
if ( isAuditMappedBy ) {
|
||||||
// If a property had the @AuditMappedBy annotation, setting the referenced fields to be always insertable.
|
// If a property had the @AuditMappedBy annotation, setting the referenced fields to be always insertable.
|
||||||
setAuditMappedByInsertable( referencedEntityName, pc.getEntityName(), referencedAuditData, propertyAuditingData );
|
setAuditMappedByInsertable( referencedEntityName, entityName, referencedAuditData, propertyAuditingData );
|
||||||
}
|
}
|
||||||
else if ( isRelationMappedBy && ( property.getValue() instanceof List ) ) {
|
else if ( isRelationMappedBy && ( property.getValue() instanceof List ) ) {
|
||||||
// If a property has mappedBy= and @Indexed and isn't @AuditMappedBy, add synthetic support.
|
// If a property has mappedBy= and @Indexed and isn't @AuditMappedBy, add synthetic support.
|
||||||
|
@ -113,6 +111,18 @@ public class ClassesAuditingData {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HHH-9108
|
||||||
|
// Added support to handle nested property calculations for components.
|
||||||
|
// This is useful for AuditMappedBy inside an Embeddable that holds a collection of entities.
|
||||||
|
if ( propertyAuditingData instanceof ComponentAuditingData ) {
|
||||||
|
final ComponentAuditingData componentAuditingData = ( ComponentAuditingData) propertyAuditingData;
|
||||||
|
final Component component = (Component) property.getValue();
|
||||||
|
for ( String componentPropertyName : componentAuditingData.getNonSyntheticPropertyNames() ) {
|
||||||
|
final Property componentProperty = component.getProperty( componentPropertyName );
|
||||||
|
updateCalculatedProperty( entityName, componentProperty, componentPropertyName, componentAuditingData );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setAuditMappedByInsertable(
|
private void setAuditMappedByInsertable(
|
||||||
|
|
|
@ -80,6 +80,13 @@ public class ClassAuditingData implements AuditedPropertiesHolder {
|
||||||
return properties.containsKey( propertyName );
|
return properties.containsKey( propertyName );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Iterable<String> getNonSyntheticPropertyNames() {
|
||||||
|
return properties.entrySet().stream()
|
||||||
|
.filter( e -> !e.getValue().isSyntheic() )
|
||||||
|
.map( Map.Entry::getKey )
|
||||||
|
.collect( Collectors.toList() );
|
||||||
|
}
|
||||||
|
|
||||||
public Iterable<PropertyAuditingData> getSyntheticProperties() {
|
public Iterable<PropertyAuditingData> getSyntheticProperties() {
|
||||||
return properties.values().stream()
|
return properties.values().stream()
|
||||||
.filter( p -> p.isSyntheic() )
|
.filter( p -> p.isSyntheic() )
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.envers.configuration.internal.metadata.reader;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.hibernate.envers.internal.tools.Tools.newHashMap;
|
import static org.hibernate.envers.internal.tools.Tools.newHashMap;
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ import static org.hibernate.envers.internal.tools.Tools.newHashMap;
|
||||||
*
|
*
|
||||||
* @author Adam Warski (adam at warski dot org)
|
* @author Adam Warski (adam at warski dot org)
|
||||||
* @author Hern&aacut;n Chanfreau
|
* @author Hern&aacut;n Chanfreau
|
||||||
|
* @author Chris Cranford
|
||||||
*/
|
*/
|
||||||
public class ComponentAuditingData extends PropertyAuditingData implements AuditedPropertiesHolder {
|
public class ComponentAuditingData extends PropertyAuditingData implements AuditedPropertiesHolder {
|
||||||
private final Map<String, PropertyAuditingData> properties;
|
private final Map<String, PropertyAuditingData> properties;
|
||||||
|
@ -47,4 +49,12 @@ public class ComponentAuditingData extends PropertyAuditingData implements Audit
|
||||||
public Set<String> getPropertyNames() {
|
public Set<String> getPropertyNames() {
|
||||||
return properties.keySet();
|
return properties.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Iterable<String> getNonSyntheticPropertyNames() {
|
||||||
|
return properties.entrySet().stream()
|
||||||
|
.filter( e -> !e.getValue().isSyntheic() )
|
||||||
|
.map( Map.Entry::getKey )
|
||||||
|
.collect( Collectors.toList() );
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,14 +162,22 @@ public abstract class BaseEnversCollectionEventListener extends BaseEnversEventL
|
||||||
*/
|
*/
|
||||||
private RelationDescription searchForRelationDescription(String entityName, String referencingPropertyName) {
|
private RelationDescription searchForRelationDescription(String entityName, String referencingPropertyName) {
|
||||||
final EntityConfiguration configuration = getEnversService().getEntitiesConfigurations().get( entityName );
|
final EntityConfiguration configuration = getEnversService().getEntitiesConfigurations().get( entityName );
|
||||||
final RelationDescription rd = configuration.getRelationDescription( referencingPropertyName );
|
final String propertyName = sanitizeReferencingPropertyName( referencingPropertyName );
|
||||||
|
final RelationDescription rd = configuration.getRelationDescription( propertyName );
|
||||||
if ( rd == null && configuration.getParentEntityName() != null ) {
|
if ( rd == null && configuration.getParentEntityName() != null ) {
|
||||||
return searchForRelationDescription( configuration.getParentEntityName(), referencingPropertyName );
|
return searchForRelationDescription( configuration.getParentEntityName(), propertyName );
|
||||||
}
|
}
|
||||||
|
|
||||||
return rd;
|
return rd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String sanitizeReferencingPropertyName(String propertyName) {
|
||||||
|
if ( propertyName != null && propertyName.indexOf( '.' ) != -1 ) {
|
||||||
|
return propertyName.replaceAll( "\\.", "\\_" );
|
||||||
|
}
|
||||||
|
return propertyName;
|
||||||
|
}
|
||||||
|
|
||||||
private void generateFakeBidirecationalRelationWorkUnits(
|
private void generateFakeBidirecationalRelationWorkUnits(
|
||||||
AuditProcess auditProcess,
|
AuditProcess auditProcess,
|
||||||
PersistentCollection newColl,
|
PersistentCollection newColl,
|
||||||
|
|
Loading…
Reference in New Issue