mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-06 19:36:03 +00:00
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.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 ClassAuditingData getClassAuditingData(String entityName) {
|
||||
* 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 void updateCalculatedFields() {
|
||||
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 void generateFirstPass(
|
||||
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 void generateFirstPass(
|
||||
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 @@ void addCollection() {
|
||||
|
||||
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 @@ private void addOneToManyAttached(boolean fakeOneToManyBidirectional) {
|
||||
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 @@ private void addOneToManyAttached(boolean fakeOneToManyBidirectional) {
|
||||
|
||||
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 @@ private void addOneToManyAttached(boolean fakeOneToManyBidirectional) {
|
||||
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 @@ private void addOneToManyAttached(boolean fakeOneToManyBidirectional) {
|
||||
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 @@
|
||||
* @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 boolean isAudited() {
|
||||
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.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 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 PropertyAuditingData(
|
||||
this.auditMappedBy = auditMappedBy;
|
||||
this.positionMappedBy = positionMappedBy;
|
||||
this.forceInsertable = forceInsertable;
|
||||
this.syntheic = syntheic;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@ -104,8 +135,13 @@ public void setAccessType(String accessType) {
|
||||
|
||||
public PropertyData getPropertyData() {
|
||||
return new PropertyData(
|
||||
name, beanName, accessType, store,
|
||||
usingModifiedFlag, modifiedFlagName
|
||||
name,
|
||||
beanName,
|
||||
accessType,
|
||||
store,
|
||||
usingModifiedFlag,
|
||||
modifiedFlagName,
|
||||
syntheic
|
||||
);
|
||||
}
|
||||
|
||||
@ -203,4 +239,11 @@ public void setRelationTargetAuditMode(RelationTargetAuditMode relationTargetAud
|
||||
this.relationTargetAuditMode = relationTargetAuditMode;
|
||||
}
|
||||
|
||||
public boolean isSyntheic() {
|
||||
return syntheic;
|
||||
}
|
||||
|
||||
public Value getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
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 @@
|
||||
* @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 @@ protected final void onCollectionAction(
|
||||
}
|
||||
}
|
||||
|
||||
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 @@
|
||||
* @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 void onPostRecreateCollection(PostCollectionRecreateEvent event) {
|
||||
if ( !collectionEntry.getLoadedPersister().isInverse() ) {
|
||||
onCollectionAction( event, event.getCollection(), null, collectionEntry );
|
||||
}
|
||||
else {
|
||||
onCollectionActionInversed( event, event.getCollection(), null, collectionEntry );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
* @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 void onPreUpdateCollection(PreCollectionUpdateEvent event) {
|
||||
if ( !collectionEntry.getLoadedPersister().isInverse() ) {
|
||||
onCollectionAction( event, event.getCollection(), collectionEntry.getSnapshot(), collectionEntry );
|
||||
}
|
||||
else {
|
||||
onCollectionActionInversed( event, event.getCollection(), collectionEntry.getSnapshot(), collectionEntry );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
/**
|
||||
* @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 void addToManyNotOwningRelation(
|
||||
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 void addToManyMiddleRelation(String fromPropertyName, String toEntityName
|
||||
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 void addToManyMiddleNotOwningRelation(String fromPropertyName, String map
|
||||
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 @@
|
||||
* 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 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 String getModifiedFlagPropertyName() {
|
||||
return modifiedFlagName;
|
||||
}
|
||||
|
||||
public boolean isSynthetic() {
|
||||
return synthetic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
@ -108,7 +118,8 @@ public boolean equals(Object o) {
|
||||
&& 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 int hashCode() {
|
||||
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 @@
|
||||
|
||||
/**
|
||||
* @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 static RelationDescription toOne(
|
||||
boolean ignoreNotFound) {
|
||||
return new RelationDescription(
|
||||
fromPropertyName, relationType, toEntityName, mappedByPropertyName, idMapper,
|
||||
fakeBidirectionalRelationMapper, fakeBidirectionalRelationIndexMapper, insertable, ignoreNotFound
|
||||
fakeBidirectionalRelationMapper, fakeBidirectionalRelationIndexMapper, insertable, ignoreNotFound, false
|
||||
);
|
||||
}
|
||||
|
||||
@ -48,14 +50,15 @@ public static RelationDescription toMany(
|
||||
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 @@ private 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 @@ private RelationDescription(
|
||||
this.fakeBidirectionalRelationMapper = fakeBidirectionalRelationMapper;
|
||||
this.fakeBidirectionalRelationIndexMapper = fakeBidirectionalRelationIndexMapper;
|
||||
this.insertable = insertable;
|
||||
|
||||
this.indexed = indexed;
|
||||
this.bidirectional = false;
|
||||
}
|
||||
|
||||
@ -118,6 +122,10 @@ public boolean isInsertable() {
|
||||
return insertable;
|
||||
}
|
||||
|
||||
public boolean isIndexed() {
|
||||
return indexed;
|
||||
}
|
||||
|
||||
public boolean isBidirectional() {
|
||||
return bidirectional;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
* @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 boolean mapToMapFromEntity(
|
||||
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 void mapModifiedFlagsToMapFromEntity(
|
||||
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 @@
|
||||
*
|
||||
* @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 void mapModifiedFlagsToMapFromEntity(
|
||||
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 void mapToEntityFromMap(
|
||||
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…
x
Reference in New Issue
Block a user