HHH-4694:

- if a field is non-insertable in a "fake" bidirectional relation, not storing modifications made on the non-insertable side
- also, not generating bidirectional collection changes in such case
- updating test

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18226 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Adam Warski 2009-12-15 12:52:20 +00:00
parent 3740a20550
commit ad0be2d822
7 changed files with 65 additions and 26 deletions

View File

@ -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;
}

View File

@ -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"})

View File

@ -51,31 +51,31 @@ public class EntityConfiguration {
this.relations = new HashMap<String, RelationDescription>();
}
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) {

View File

@ -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;
}

View File

@ -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<String, Object> data, Object newObj, Object oldObj) {
HashMap<String, Object> newData = new HashMap<String, Object>();
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,

View File

@ -84,7 +84,8 @@ public class AuditEventListener implements PostInsertEventListener, PostUpdateEv
for (int i=0; i<propertyNames.length; i++) {
String propertyName = propertyNames[i];
RelationDescription relDesc = verCfg.getEntCfg().getRelationDescription(entityName, propertyName);
if (relDesc != null && relDesc.isBidirectional() && relDesc.getRelationType() == RelationType.TO_ONE) {
if (relDesc != null && relDesc.isBidirectional() && relDesc.getRelationType() == RelationType.TO_ONE &&
relDesc.isInsertable()) {
// Checking for changes
Object oldValue = oldState == null ? null : oldState[i];
Object newValue = newState == null ? null : newState[i];
@ -144,11 +145,14 @@ public class AuditEventListener implements PostInsertEventListener, PostUpdateEv
if (verCfg.getEntCfg().isVersioned(entityName)) {
AuditSync verSync = verCfg.getSyncManager().get(event.getSession());
verSync.addWorkUnit(new AddWorkUnit(event.getSession(), event.getPersister().getEntityName(), verCfg,
event.getId(), event.getPersister(), event.getState()));
AuditWorkUnit workUnit = new AddWorkUnit(event.getSession(), event.getPersister().getEntityName(), verCfg,
event.getId(), event.getPersister(), event.getState());
verSync.addWorkUnit(workUnit);
generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, event.getState(),
null, event.getSession());
if (workUnit.containsWork()) {
generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, event.getState(),
null, event.getSession());
}
}
}
@ -158,11 +162,14 @@ public class AuditEventListener implements PostInsertEventListener, PostUpdateEv
if (verCfg.getEntCfg().isVersioned(entityName)) {
AuditSync verSync = verCfg.getSyncManager().get(event.getSession());
verSync.addWorkUnit(new ModWorkUnit(event.getSession(), event.getPersister().getEntityName(), verCfg,
event.getId(), event.getPersister(), event.getState(), event.getOldState()));
AuditWorkUnit workUnit = new ModWorkUnit(event.getSession(), event.getPersister().getEntityName(), verCfg,
event.getId(), event.getPersister(), event.getState(), event.getOldState());
verSync.addWorkUnit(workUnit);
generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, event.getState(),
event.getOldState(), event.getSession());
if (workUnit.containsWork()) {
generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, event.getState(),
event.getOldState(), event.getSession());
}
}
}
@ -172,11 +179,14 @@ public class AuditEventListener implements PostInsertEventListener, PostUpdateEv
if (verCfg.getEntCfg().isVersioned(entityName)) {
AuditSync verSync = verCfg.getSyncManager().get(event.getSession());
verSync.addWorkUnit(new DelWorkUnit(event.getSession(), event.getPersister().getEntityName(), verCfg,
event.getId(), event.getPersister(), event.getDeletedState()));
AuditWorkUnit workUnit = new DelWorkUnit(event.getSession(), event.getPersister().getEntityName(), verCfg,
event.getId(), event.getPersister(), event.getDeletedState());
verSync.addWorkUnit(workUnit);
generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, null,
event.getDeletedState(), event.getSession());
if (workUnit.containsWork()) {
generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, null,
event.getDeletedState(), event.getSession());
}
}
}

View File

@ -86,12 +86,27 @@ public class JoinColumnBidirectionalList extends AbstractEntityTest {
em.getTransaction().commit();
em.clear();
// No revision - no changes
em.getTransaction().begin();
ing1 = em.find(ListJoinColumnBidirectionalRefIngEntity.class, ing1.getId());
ing2 = em.find(ListJoinColumnBidirectionalRefIngEntity.class, ing2.getId());
ed1 = em.find(ListJoinColumnBidirectionalRefEdEntity.class, ed1.getId());
ed2 = em.find(ListJoinColumnBidirectionalRefEdEntity.class, ed2.getId());
ed2.setOwner(ing2);
em.getTransaction().commit();
em.clear();
// Revision 3 (ing1: ed1, ed2)
em.getTransaction().begin();
ed1 = em.find(ListJoinColumnBidirectionalRefEdEntity.class, ed1.getId());
ed1.setData("ed1 bis");
// Shouldn't get written
ed1.setOwner(ing2);
em.getTransaction().commit();
em.clear();