HHH-7940 - Fix NullPointerException when using IndexColumn/OrderColumn without AuditMappedBy.
This commit is contained in:
parent
94f2401b3c
commit
8db194e8a6
|
@ -12,18 +12,24 @@ import java.util.LinkedHashMap;
|
|||
import java.util.Map;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.envers.ModificationStore;
|
||||
import org.hibernate.envers.RelationTargetAuditMode;
|
||||
import org.hibernate.envers.configuration.internal.metadata.reader.ClassAuditingData;
|
||||
import org.hibernate.envers.configuration.internal.metadata.reader.PropertyAuditingData;
|
||||
import org.hibernate.envers.internal.EnversMessageLogger;
|
||||
import org.hibernate.envers.internal.tools.MappingTools;
|
||||
import org.hibernate.mapping.List;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.mapping.Value;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* A helper class holding auditing meta-data for all persistent classes.
|
||||
*
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class ClassesAuditingData {
|
||||
private static final EnversMessageLogger LOG = Logger.getMessageLogger(
|
||||
|
@ -65,6 +71,7 @@ public class ClassesAuditingData {
|
|||
* 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>
|
||||
* <li>adding {@code synthetic} properties to mappedBy relations which have {@code IndexColumn} or {@code OrderColumn}.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public void updateCalculatedFields() {
|
||||
|
@ -72,25 +79,79 @@ public class ClassesAuditingData {
|
|||
final PersistentClass pc = classAuditingDataEntry.getKey();
|
||||
final ClassAuditingData classAuditingData = classAuditingDataEntry.getValue();
|
||||
for ( String propertyName : classAuditingData.getPropertyNames() ) {
|
||||
final PropertyAuditingData propertyAuditingData = classAuditingData.getPropertyAuditingData( propertyName );
|
||||
updateCalculatedProperty( pc, classAuditingData, propertyName );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCalculatedProperty(
|
||||
PersistentClass pc,
|
||||
ClassAuditingData classAuditingData,
|
||||
String propertyName) {
|
||||
|
||||
final PropertyAuditingData propertyAuditingData = classAuditingData.getPropertyAuditingData( propertyName );
|
||||
|
||||
final boolean isAuditMappedBy = propertyAuditingData.getAuditMappedBy() != null;
|
||||
final boolean isRelationMappedBy = propertyAuditingData.getRelationMappedBy() != null;
|
||||
|
||||
if ( isAuditMappedBy || isRelationMappedBy ) {
|
||||
final Property property = pc.getProperty( propertyName );
|
||||
final String referencedEntityName = MappingTools.getReferencedEntityName( property.getValue() );
|
||||
|
||||
final ClassAuditingData referencedAuditData = entityNameToAuditingData.get( referencedEntityName );
|
||||
|
||||
if ( isAuditMappedBy ) {
|
||||
// If a property had the @AuditMappedBy annotation, setting the referenced fields to be always insertable.
|
||||
if ( propertyAuditingData.getAuditMappedBy() != null ) {
|
||||
final String referencedEntityName = MappingTools.getReferencedEntityName(
|
||||
pc.getProperty( propertyName ).getValue()
|
||||
);
|
||||
setAuditMappedByInsertable( referencedEntityName, pc.getEntityName(), referencedAuditData, propertyAuditingData );
|
||||
}
|
||||
else if ( isRelationMappedBy && ( property.getValue() instanceof List ) ) {
|
||||
// If a property has mappedBy= and @Indexed and isn't @AuditMappedBy, add synthetic support.
|
||||
addSyntheticIndexProperty(
|
||||
(List) property.getValue(),
|
||||
property.getPropertyAccessorName(),
|
||||
referencedAuditData
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final ClassAuditingData referencedClassAuditingData = entityNameToAuditingData.get( referencedEntityName );
|
||||
private void setAuditMappedByInsertable(
|
||||
String referencedEntityName,
|
||||
String entityName,
|
||||
ClassAuditingData referencedAuditData,
|
||||
PropertyAuditingData propertyAuditingData) {
|
||||
forcePropertyInsertable(
|
||||
referencedAuditData,
|
||||
propertyAuditingData.getAuditMappedBy(),
|
||||
entityName,
|
||||
referencedEntityName
|
||||
);
|
||||
|
||||
forcePropertyInsertable(
|
||||
referencedClassAuditingData, propertyAuditingData.getAuditMappedBy(),
|
||||
pc.getEntityName(), referencedEntityName
|
||||
);
|
||||
forcePropertyInsertable(
|
||||
referencedAuditData,
|
||||
propertyAuditingData.getPositionMappedBy(),
|
||||
entityName,
|
||||
referencedEntityName
|
||||
);
|
||||
}
|
||||
|
||||
forcePropertyInsertable(
|
||||
referencedClassAuditingData, propertyAuditingData.getPositionMappedBy(),
|
||||
pc.getEntityName(), referencedEntityName
|
||||
);
|
||||
}
|
||||
private void addSyntheticIndexProperty(List value, String propertyAccessorName, ClassAuditingData classAuditingData) {
|
||||
final Value indexValue = value.getIndex();
|
||||
if ( indexValue != null && indexValue.getColumnIterator().hasNext() ) {
|
||||
final String indexColumnName = indexValue.getColumnIterator().next().getText();
|
||||
if ( indexColumnName != null ) {
|
||||
final PropertyAuditingData auditingData = new PropertyAuditingData(
|
||||
indexColumnName,
|
||||
propertyAccessorName,
|
||||
ModificationStore.FULL,
|
||||
RelationTargetAuditMode.AUDITED,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
true,
|
||||
indexValue
|
||||
);
|
||||
classAuditingData.addPropertyAuditingData( indexColumnName, auditingData );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -694,6 +694,9 @@ public final class AuditMetadataGenerator {
|
|||
createJoins( pc, classMapping, auditingData );
|
||||
addJoins( pc, propertyMapper, auditingData, pc.getEntityName(), xmlMappingData, true );
|
||||
|
||||
// HHH-7940 - New synthetic property support for @IndexColumn/@OrderColumn dynamic properties
|
||||
addSynthetics( classMapping, auditingData, propertyMapper, xmlMappingData, pc.getEntityName(), true );
|
||||
|
||||
// Storing the generated configuration
|
||||
final EntityConfiguration entityCfg = new EntityConfiguration(
|
||||
auditEntityName,
|
||||
|
@ -705,6 +708,28 @@ public final class AuditMetadataGenerator {
|
|||
entitiesConfigurations.put( pc.getEntityName(), entityCfg );
|
||||
}
|
||||
|
||||
private void addSynthetics(
|
||||
Element classMapping,
|
||||
ClassAuditingData auditingData,
|
||||
CompositeMapperBuilder currentMapper,
|
||||
EntityXmlMappingData xmlMappingData,
|
||||
String entityName,
|
||||
boolean firstPass) {
|
||||
for ( PropertyAuditingData propertyAuditingData : auditingData.getSyntheticProperties() ) {
|
||||
addValue(
|
||||
classMapping,
|
||||
propertyAuditingData.getValue(),
|
||||
currentMapper,
|
||||
entityName,
|
||||
xmlMappingData,
|
||||
propertyAuditingData,
|
||||
true,
|
||||
firstPass,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public void generateSecondPass(
|
||||
PersistentClass pc,
|
||||
|
|
|
@ -157,7 +157,7 @@ public final class CollectionMetadataGenerator {
|
|||
|
||||
if ( oneToManyAttachedType && (inverseOneToMany || fakeOneToManyBidirectional || owningManyToOneWithJoinTableBidirectional) ) {
|
||||
// A one-to-many relation mapped using @ManyToOne and @OneToMany(mappedBy="...")
|
||||
addOneToManyAttached( fakeOneToManyBidirectional );
|
||||
addOneToManyAttached( fakeOneToManyBidirectional );
|
||||
}
|
||||
else {
|
||||
// All other kinds of relations require a middle (join) table.
|
||||
|
@ -183,6 +183,10 @@ public final class CollectionMetadataGenerator {
|
|||
propertyName
|
||||
);
|
||||
|
||||
// check whether the property has an @IndexColumn or @OrderColumn because its part of an
|
||||
// IndexedCollection mapping type.
|
||||
final boolean indexed = ( propertyValue instanceof IndexedCollection ) && ( (IndexedCollection) propertyValue ).getIndex() != null;
|
||||
|
||||
final String mappedBy = getMappedBy( propertyValue );
|
||||
|
||||
final IdMappingData referencedIdMapping = mainGenerator.getReferencedIdMappingData(
|
||||
|
@ -239,10 +243,16 @@ public final class CollectionMetadataGenerator {
|
|||
|
||||
PropertyMapper fakeBidirectionalRelationMapper;
|
||||
PropertyMapper fakeBidirectionalRelationIndexMapper;
|
||||
if ( fakeOneToManyBidirectional ) {
|
||||
if ( fakeOneToManyBidirectional || indexed ) {
|
||||
// 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).
|
||||
final String auditMappedBy = propertyAuditingData.getAuditMappedBy();
|
||||
final String auditMappedBy;
|
||||
if ( fakeOneToManyBidirectional ) {
|
||||
auditMappedBy = propertyAuditingData.getAuditMappedBy();
|
||||
}
|
||||
else {
|
||||
auditMappedBy = propertyValue.getMappedByProperty();
|
||||
}
|
||||
|
||||
// Creating a prefixed relation mapper.
|
||||
final IdMapper relMapper = referencingIdMapping.getIdMapper().prefixMappedProperties(
|
||||
|
@ -257,9 +267,20 @@ public final class CollectionMetadataGenerator {
|
|||
referencingEntityName, false
|
||||
);
|
||||
|
||||
final String positionMappedBy;
|
||||
if ( fakeOneToManyBidirectional ) {
|
||||
positionMappedBy = propertyAuditingData.getPositionMappedBy();
|
||||
}
|
||||
else if ( indexed ) {
|
||||
final Value indexValue = ( (IndexedCollection) propertyValue ).getIndex();
|
||||
positionMappedBy = indexValue.getColumnIterator().next().getText();
|
||||
}
|
||||
else {
|
||||
positionMappedBy = null;
|
||||
}
|
||||
|
||||
// Checking if there's an index defined. If so, adding a mapper for it.
|
||||
if ( propertyAuditingData.getPositionMappedBy() != null ) {
|
||||
final String positionMappedBy = propertyAuditingData.getPositionMappedBy();
|
||||
if ( positionMappedBy != null ) {
|
||||
fakeBidirectionalRelationIndexMapper = new SinglePropertyMapper(
|
||||
new PropertyData(
|
||||
positionMappedBy,
|
||||
|
@ -294,7 +315,8 @@ public final class CollectionMetadataGenerator {
|
|||
referencedEntityName,
|
||||
referencingIdData.getPrefixedMapper(),
|
||||
fakeBidirectionalRelationMapper,
|
||||
fakeBidirectionalRelationIndexMapper
|
||||
fakeBidirectionalRelationIndexMapper,
|
||||
indexed
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.envers.configuration.internal.metadata.reader;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.hibernate.envers.AuditTable;
|
||||
|
||||
|
@ -16,6 +17,7 @@ import static org.hibernate.envers.internal.tools.Tools.newHashMap;
|
|||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Sebastian Komander
|
||||
* @author Hern&aacut;n Chanfreau
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class ClassAuditingData implements AuditedPropertiesHolder {
|
||||
private final Map<String, PropertyAuditingData> properties;
|
||||
|
@ -77,4 +79,10 @@ public class ClassAuditingData implements AuditedPropertiesHolder {
|
|||
public boolean contains(String propertyName) {
|
||||
return properties.containsKey( propertyName );
|
||||
}
|
||||
|
||||
public Iterable<PropertyAuditingData> getSyntheticProperties() {
|
||||
return properties.values().stream()
|
||||
.filter( p -> p.isSyntheic() )
|
||||
.collect( Collectors.toList() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,12 @@ import org.hibernate.envers.AuditOverrides;
|
|||
import org.hibernate.envers.ModificationStore;
|
||||
import org.hibernate.envers.RelationTargetAuditMode;
|
||||
import org.hibernate.envers.internal.entities.PropertyData;
|
||||
import org.hibernate.mapping.Value;
|
||||
|
||||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Michal Skowronek (mskowr at o2 dot pl)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class PropertyAuditingData {
|
||||
private String name;
|
||||
|
@ -35,6 +37,10 @@ public class PropertyAuditingData {
|
|||
private boolean forceInsertable;
|
||||
private boolean usingModifiedFlag;
|
||||
private String modifiedFlagName;
|
||||
private Value value;
|
||||
// Synthetic properties are ones which are not part of the actual java model.
|
||||
// They're properties used for bookkeeping by Hibernate
|
||||
private boolean syntheic;
|
||||
|
||||
public PropertyAuditingData() {
|
||||
}
|
||||
|
@ -44,6 +50,29 @@ public class PropertyAuditingData {
|
|||
RelationTargetAuditMode relationTargetAuditMode,
|
||||
String auditMappedBy, String positionMappedBy,
|
||||
boolean forceInsertable) {
|
||||
this(
|
||||
name,
|
||||
accessType,
|
||||
store,
|
||||
relationTargetAuditMode,
|
||||
auditMappedBy,
|
||||
positionMappedBy,
|
||||
forceInsertable,
|
||||
false,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
public PropertyAuditingData(
|
||||
String name,
|
||||
String accessType,
|
||||
ModificationStore store,
|
||||
RelationTargetAuditMode relationTargetAuditMode,
|
||||
String auditMappedBy,
|
||||
String positionMappedBy,
|
||||
boolean forceInsertable,
|
||||
boolean syntheic,
|
||||
Value value) {
|
||||
this.name = name;
|
||||
this.beanName = name;
|
||||
this.accessType = accessType;
|
||||
|
@ -52,6 +81,8 @@ public class PropertyAuditingData {
|
|||
this.auditMappedBy = auditMappedBy;
|
||||
this.positionMappedBy = positionMappedBy;
|
||||
this.forceInsertable = forceInsertable;
|
||||
this.syntheic = syntheic;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
@ -104,8 +135,13 @@ public class PropertyAuditingData {
|
|||
|
||||
public PropertyData getPropertyData() {
|
||||
return new PropertyData(
|
||||
name, beanName, accessType, store,
|
||||
usingModifiedFlag, modifiedFlagName
|
||||
name,
|
||||
beanName,
|
||||
accessType,
|
||||
store,
|
||||
usingModifiedFlag,
|
||||
modifiedFlagName,
|
||||
syntheic
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -203,4 +239,11 @@ public class PropertyAuditingData {
|
|||
this.relationTargetAuditMode = relationTargetAuditMode;
|
||||
}
|
||||
|
||||
public boolean isSyntheic() {
|
||||
return syntheic;
|
||||
}
|
||||
|
||||
public Value getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.envers.RevisionType;
|
|||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.internal.entities.EntityConfiguration;
|
||||
import org.hibernate.envers.internal.entities.RelationDescription;
|
||||
import org.hibernate.envers.internal.entities.RelationType;
|
||||
import org.hibernate.envers.internal.entities.mapper.PersistentCollectionChangeData;
|
||||
import org.hibernate.envers.internal.entities.mapper.id.IdMapper;
|
||||
import org.hibernate.envers.internal.synchronization.AuditProcess;
|
||||
|
@ -34,6 +35,7 @@ import org.hibernate.persister.collection.AbstractCollectionPersister;
|
|||
* @author Steve Ebersole
|
||||
* @author Michal Skowronek (mskowr at o2 dot pl)
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public abstract class BaseEnversCollectionEventListener extends BaseEnversEventListener {
|
||||
protected BaseEnversCollectionEventListener(EnversService enversService) {
|
||||
|
@ -104,6 +106,25 @@ public abstract class BaseEnversCollectionEventListener extends BaseEnversEventL
|
|||
}
|
||||
}
|
||||
|
||||
protected final void onCollectionActionInversed(
|
||||
AbstractCollectionEvent event,
|
||||
PersistentCollection newColl,
|
||||
Serializable oldColl,
|
||||
CollectionEntry collectionEntry) {
|
||||
if ( shouldGenerateRevision( event ) ) {
|
||||
final String entityName = event.getAffectedOwnerEntityName();
|
||||
final String ownerEntityName = ( (AbstractCollectionPersister) collectionEntry.getLoadedPersister() ).getOwnerEntityName();
|
||||
final String referencingPropertyName = collectionEntry.getRole().substring( ownerEntityName.length() + 1 );
|
||||
|
||||
final RelationDescription rd = searchForRelationDescription( entityName, referencingPropertyName );
|
||||
if ( rd != null ) {
|
||||
if ( rd.getRelationType().equals( RelationType.TO_MANY_NOT_OWNING ) && rd.isIndexed() ) {
|
||||
onCollectionAction( event, newColl, oldColl, collectionEntry );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces persistent collection initialization.
|
||||
*
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.hibernate.event.spi.PostCollectionRecreateEventListener;
|
|||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author HernпїЅn Chanfreau
|
||||
* @author Steve Ebersole
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class EnversPostCollectionRecreateEventListenerImpl
|
||||
extends BaseEnversCollectionEventListener
|
||||
|
@ -32,5 +33,8 @@ public class EnversPostCollectionRecreateEventListenerImpl
|
|||
if ( !collectionEntry.getLoadedPersister().isInverse() ) {
|
||||
onCollectionAction( event, event.getCollection(), null, collectionEntry );
|
||||
}
|
||||
else {
|
||||
onCollectionActionInversed( event, event.getCollection(), null, collectionEntry );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.hibernate.event.spi.PreCollectionUpdateEventListener;
|
|||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author HernпїЅn Chanfreau
|
||||
* @author Steve Ebersole
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class EnversPreCollectionUpdateEventListenerImpl
|
||||
extends BaseEnversCollectionEventListener
|
||||
|
@ -32,5 +33,8 @@ public class EnversPreCollectionUpdateEventListenerImpl
|
|||
if ( !collectionEntry.getLoadedPersister().isInverse() ) {
|
||||
onCollectionAction( event, event.getCollection(), collectionEntry.getSnapshot(), collectionEntry );
|
||||
}
|
||||
else {
|
||||
onCollectionActionInversed( event, event.getCollection(), collectionEntry.getSnapshot(), collectionEntry );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.envers.internal.entities.mapper.id.IdMapper;
|
|||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author HernпїЅn Chanfreau
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class EntityConfiguration {
|
||||
private String versionsEntityName;
|
||||
|
@ -80,12 +81,13 @@ public class EntityConfiguration {
|
|||
String toEntityName,
|
||||
IdMapper idMapper,
|
||||
PropertyMapper fakeBidirectionalRelationMapper,
|
||||
PropertyMapper fakeBidirectionalRelationIndexMapper) {
|
||||
PropertyMapper fakeBidirectionalRelationIndexMapper,
|
||||
boolean indexed) {
|
||||
relations.put(
|
||||
fromPropertyName,
|
||||
RelationDescription.toMany(
|
||||
fromPropertyName, RelationType.TO_MANY_NOT_OWNING, toEntityName, mappedByPropertyName,
|
||||
idMapper, fakeBidirectionalRelationMapper, fakeBidirectionalRelationIndexMapper, true
|
||||
idMapper, fakeBidirectionalRelationMapper, fakeBidirectionalRelationIndexMapper, true, indexed
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -94,7 +96,7 @@ public class EntityConfiguration {
|
|||
relations.put(
|
||||
fromPropertyName,
|
||||
RelationDescription.toMany(
|
||||
fromPropertyName, RelationType.TO_MANY_MIDDLE, toEntityName, null, null, null, null, true
|
||||
fromPropertyName, RelationType.TO_MANY_MIDDLE, toEntityName, null, null, null, null, true, false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -104,7 +106,7 @@ public class EntityConfiguration {
|
|||
fromPropertyName,
|
||||
RelationDescription.toMany(
|
||||
fromPropertyName, RelationType.TO_MANY_MIDDLE_NOT_OWNING, toEntityName, mappedByPropertyName,
|
||||
null, null, null, true
|
||||
null, null, null, true, false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.hibernate.internal.util.compare.EqualsHelper;
|
|||
* Holds information on a property that is audited.
|
||||
*
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class PropertyData {
|
||||
private final String name;
|
||||
|
@ -24,6 +25,9 @@ public class PropertyData {
|
|||
private final ModificationStore store;
|
||||
private boolean usingModifiedFlag;
|
||||
private String modifiedFlagName;
|
||||
// Synthetic properties are ones which are not part of the actual java model.
|
||||
// They're properties used for bookkeeping by Hibernate
|
||||
private boolean synthetic;
|
||||
|
||||
/**
|
||||
* Copies the given property data, except the name.
|
||||
|
@ -64,10 +68,12 @@ public class PropertyData {
|
|||
String accessType,
|
||||
ModificationStore store,
|
||||
boolean usingModifiedFlag,
|
||||
String modifiedFlagName) {
|
||||
String modifiedFlagName,
|
||||
boolean synthetic) {
|
||||
this( name, beanName, accessType, store );
|
||||
this.usingModifiedFlag = usingModifiedFlag;
|
||||
this.modifiedFlagName = modifiedFlagName;
|
||||
this.synthetic = synthetic;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
@ -94,6 +100,10 @@ public class PropertyData {
|
|||
return modifiedFlagName;
|
||||
}
|
||||
|
||||
public boolean isSynthetic() {
|
||||
return synthetic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
|
@ -108,7 +118,8 @@ public class PropertyData {
|
|||
&& store == that.store
|
||||
&& EqualsHelper.equals( accessType, that.accessType )
|
||||
&& EqualsHelper.equals( beanName, that.beanName )
|
||||
&& EqualsHelper.equals( name, that.name );
|
||||
&& EqualsHelper.equals( name, that.name )
|
||||
&& EqualsHelper.equals( synthetic, that.synthetic );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -118,6 +129,7 @@ public class PropertyData {
|
|||
result = 31 * result + (accessType != null ? accessType.hashCode() : 0);
|
||||
result = 31 * result + (store != null ? store.hashCode() : 0);
|
||||
result = 31 * result + (usingModifiedFlag ? 1 : 0);
|
||||
result = 31 * result + (synthetic ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.hibernate.envers.internal.entities.mapper.id.IdMapper;
|
|||
|
||||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class RelationDescription {
|
||||
private final String fromPropertyName;
|
||||
|
@ -22,6 +23,7 @@ public class RelationDescription {
|
|||
private final PropertyMapper fakeBidirectionalRelationMapper;
|
||||
private final PropertyMapper fakeBidirectionalRelationIndexMapper;
|
||||
private final boolean insertable;
|
||||
private final boolean indexed;
|
||||
private boolean bidirectional;
|
||||
|
||||
public static RelationDescription toOne(
|
||||
|
@ -36,7 +38,7 @@ public class RelationDescription {
|
|||
boolean ignoreNotFound) {
|
||||
return new RelationDescription(
|
||||
fromPropertyName, relationType, toEntityName, mappedByPropertyName, idMapper,
|
||||
fakeBidirectionalRelationMapper, fakeBidirectionalRelationIndexMapper, insertable, ignoreNotFound
|
||||
fakeBidirectionalRelationMapper, fakeBidirectionalRelationIndexMapper, insertable, ignoreNotFound, false
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -48,14 +50,15 @@ public class RelationDescription {
|
|||
IdMapper idMapper,
|
||||
PropertyMapper fakeBidirectionalRelationMapper,
|
||||
PropertyMapper fakeBidirectionalRelationIndexMapper,
|
||||
boolean insertable) {
|
||||
boolean insertable,
|
||||
boolean indexed) {
|
||||
// Envers populates collections by executing dedicated queries. Special handling of
|
||||
// @NotFound(action = NotFoundAction.IGNORE) can be omitted in such case as exceptions
|
||||
// (e.g. EntityNotFoundException, ObjectNotFoundException) are never thrown.
|
||||
// Therefore assigning false to ignoreNotFound.
|
||||
return new RelationDescription(
|
||||
fromPropertyName, relationType, toEntityName, mappedByPropertyName, idMapper, fakeBidirectionalRelationMapper,
|
||||
fakeBidirectionalRelationIndexMapper, insertable, false
|
||||
fakeBidirectionalRelationIndexMapper, insertable, false, indexed
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -68,7 +71,8 @@ public class RelationDescription {
|
|||
PropertyMapper fakeBidirectionalRelationMapper,
|
||||
PropertyMapper fakeBidirectionalRelationIndexMapper,
|
||||
boolean insertable,
|
||||
boolean ignoreNotFound) {
|
||||
boolean ignoreNotFound,
|
||||
boolean indexed) {
|
||||
this.fromPropertyName = fromPropertyName;
|
||||
this.relationType = relationType;
|
||||
this.toEntityName = toEntityName;
|
||||
|
@ -78,7 +82,7 @@ public class RelationDescription {
|
|||
this.fakeBidirectionalRelationMapper = fakeBidirectionalRelationMapper;
|
||||
this.fakeBidirectionalRelationIndexMapper = fakeBidirectionalRelationIndexMapper;
|
||||
this.insertable = insertable;
|
||||
|
||||
this.indexed = indexed;
|
||||
this.bidirectional = false;
|
||||
}
|
||||
|
||||
|
@ -118,6 +122,10 @@ public class RelationDescription {
|
|||
return insertable;
|
||||
}
|
||||
|
||||
public boolean isIndexed() {
|
||||
return indexed;
|
||||
}
|
||||
|
||||
public boolean isBidirectional() {
|
||||
return bidirectional;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.hibernate.property.access.spi.Getter;
|
|||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Michal Skowronek (mskowr at o2 dot pl)
|
||||
* @author Lukasz Zuchowski (author at zuchos dot com)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class MultiPropertyMapper implements ExtendedPropertyMapper {
|
||||
protected final Map<PropertyData, PropertyMapper> properties;
|
||||
|
@ -102,6 +103,12 @@ public class MultiPropertyMapper implements ExtendedPropertyMapper {
|
|||
for ( Map.Entry<PropertyData, PropertyMapper> entry : properties.entrySet() ) {
|
||||
final PropertyData propertyData = entry.getKey();
|
||||
final PropertyMapper propertyMapper = entry.getValue();
|
||||
|
||||
// synthetic properties are not part of the entity model; therefore they should be ignored.
|
||||
if ( propertyData.isSynthetic() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Getter getter;
|
||||
if ( newObj != null ) {
|
||||
getter = ReflectionTools.getGetter( newObj.getClass(), propertyData, session.getFactory().getServiceRegistry() );
|
||||
|
@ -131,6 +138,12 @@ public class MultiPropertyMapper implements ExtendedPropertyMapper {
|
|||
for ( Map.Entry<PropertyData, PropertyMapper> entry : properties.entrySet() ) {
|
||||
final PropertyData propertyData = entry.getKey();
|
||||
final PropertyMapper propertyMapper = entry.getValue();
|
||||
|
||||
// synthetic properties are not part of the entity model; therefore they should be ignored.
|
||||
if ( propertyData.isSynthetic() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Getter getter;
|
||||
if ( newObj != null ) {
|
||||
getter = ReflectionTools.getGetter( newObj.getClass(), propertyData, session.getFactory().getServiceRegistry() );
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.hibernate.property.access.spi.SetterFieldImpl;
|
|||
*
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Michal Skowronek (mskowr at o2 dot pl)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class SinglePropertyMapper implements PropertyMapper, SimpleMapperBuilder {
|
||||
private PropertyData propertyData;
|
||||
|
@ -71,7 +72,8 @@ public class SinglePropertyMapper implements PropertyMapper, SimpleMapperBuilder
|
|||
Map<String, Object> data,
|
||||
Object newObj,
|
||||
Object oldObj) {
|
||||
if ( propertyData.isUsingModifiedFlag() ) {
|
||||
// Synthetic properties are not subject to withModifiedFlag analysis
|
||||
if ( propertyData.isUsingModifiedFlag() && !propertyData.isSynthetic() ) {
|
||||
data.put( propertyData.getModifiedFlagPropertyName(), !EqualsHelper.areEqual( newObj, oldObj ) );
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +90,8 @@ public class SinglePropertyMapper implements PropertyMapper, SimpleMapperBuilder
|
|||
Object primaryKey,
|
||||
AuditReaderImplementor versionsReader,
|
||||
Number revision) {
|
||||
if ( data == null || obj == null ) {
|
||||
// synthetic properties are not part of the entity model; therefore they should be ignored.
|
||||
if ( data == null || obj == null || propertyData.isSynthetic() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue