Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gsmet/e18ccb7d3f3494bb334647e540d0d4d0 to your computer and use it in GitHub Desktop.
Save gsmet/e18ccb7d3f3494bb334647e540d0d4d0 to your computer and use it in GitHub Desktop.
diff --git a/core/src/main/java/org/hibernate/ogm/entityentry/impl/OgmEntityEntryState.java b/core/src/main/java/org/hibernate/ogm/entityentry/impl/OgmEntityEntryState.java
index 64f8236..47a8944 100644
--- a/core/src/main/java/org/hibernate/ogm/entityentry/impl/OgmEntityEntryState.java
+++ b/core/src/main/java/org/hibernate/ogm/entityentry/impl/OgmEntityEntryState.java
@@ -29,7 +29,16 @@
private EntityEntryExtraState next;
private final TuplePointer tuplePointer = new TuplePointer();
+
+ /**
+ * As for the associations, we store in the state of an entity the associations for which this entity is the owner
+ * of the collection (not necessarily the association owner). We also store in the entity, the inverse associations
+ * for which this entity is the association owner.
+ *
+ * The entity porting the inverse side of the association does not store any {@link Association}s here.
+ */
private Map<String, Association> associations;
+ private Map<String, Association> inverseAssociations;
/**
* Return the stable pointer to the {@link Tuple} representing the given entity, as loaded from the datastore.
@@ -79,6 +88,45 @@ public void setAssociation(String collectionRole, Association association) {
associations.put( collectionRole, association );
}
+ /**
+ * Return the inverse association as cached in the entry state.
+ *
+ * @param collectionRole the role of the inverse association
+ * @return the cached inverse association
+ */
+ public Association getInverseAssociation(String collectionRole) {
+ if ( inverseAssociations == null ) {
+ return null;
+ }
+ return inverseAssociations.get( collectionRole );
+ }
+
+ /**
+ * Indicates if the entry state contains information about the given inverse association.
+ *
+ * @param collectionRole the role of the inverse association
+ * @return true if the entry state contains information about the given inverse association
+ */
+ public boolean hasInverseAssociation(String collectionRole) {
+ if ( inverseAssociations == null ) {
+ return false;
+ }
+ return inverseAssociations.containsKey( collectionRole );
+ }
+
+ /**
+ * Set the inverse association in the entry state.
+ *
+ * @param collectionRole the role of the inverse association
+ * @param association the inverse association
+ */
+ public void setInverseAssociation(String collectionRole, Association association) {
+ if ( inverseAssociations == null ) {
+ inverseAssociations = new HashMap<>();
+ }
+ inverseAssociations.put( collectionRole, association );
+ }
+
public static OgmEntityEntryState getStateFor(SessionImplementor session, Object object) {
EntityEntry entityEntry = session.getPersistenceContext().getEntry( object );
if ( entityEntry == null ) {
diff --git a/core/src/main/java/org/hibernate/ogm/loader/impl/OgmLoader.java b/core/src/main/java/org/hibernate/ogm/loader/impl/OgmLoader.java
index a8500c5..1ce2b0e 100644
--- a/core/src/main/java/org/hibernate/ogm/loader/impl/OgmLoader.java
+++ b/core/src/main/java/org/hibernate/ogm/loader/impl/OgmLoader.java
@@ -599,6 +599,7 @@ private TupleAsMapResultSet getResultSet(Serializable id, QueryParameters qp, Og
final OgmCollectionPersister persister = (OgmCollectionPersister) getCollectionPersisters()[0];
Object owner = session.getPersistenceContext().getCollectionOwner( id, persister );
+ // XXX determine the association owner?
AssociationPersister associationPersister = new AssociationPersister(
persister.getOwnerEntityPersister().getMappedClass()
)
diff --git a/core/src/main/java/org/hibernate/ogm/persister/impl/EntityAssociationUpdater.java b/core/src/main/java/org/hibernate/ogm/persister/impl/EntityAssociationUpdater.java
index 0b875f5..14d265f 100644
--- a/core/src/main/java/org/hibernate/ogm/persister/impl/EntityAssociationUpdater.java
+++ b/core/src/main/java/org/hibernate/ogm/persister/impl/EntityAssociationUpdater.java
@@ -45,6 +45,7 @@
private final OgmEntityPersister persister;
private final GridDialect gridDialect;
+ private Object associationOwner;
private Tuple resultset;
private int tableIndex;
private Serializable id;
@@ -58,6 +59,11 @@
// fluent methods populating data
+ public EntityAssociationUpdater associationOwner(Object associationOwner) {
+ this.associationOwner = associationOwner;
+ return this;
+ }
+
/**
* Sets the tuple representing the entity whose inverse associations should be updated.
*/
@@ -197,6 +203,7 @@ private AssociationPersister createAssociationPersister(int propertyIndex, Assoc
return new AssociationPersister(
persister.getPropertyTypes()[propertyIndex].getReturnedClass()
)
+ .associationOwner( associationOwner )
.hostingEntity( getReferencedEntity( propertyIndex ) )
.gridDialect( gridDialect )
.associationKeyMetadata( associationKeyMetadata )
diff --git a/core/src/main/java/org/hibernate/ogm/persister/impl/OgmCollectionPersister.java b/core/src/main/java/org/hibernate/ogm/persister/impl/OgmCollectionPersister.java
index 1b32f7c..26edda5 100644
--- a/core/src/main/java/org/hibernate/ogm/persister/impl/OgmCollectionPersister.java
+++ b/core/src/main/java/org/hibernate/ogm/persister/impl/OgmCollectionPersister.java
@@ -76,6 +76,7 @@
* CollectionPersister storing the collection in a grid
*
* @author Emmanuel Bernard
+ * @author Guillaume Smet
*/
public class OgmCollectionPersister extends AbstractCollectionPersister implements CollectionPhysicalModel {
@@ -339,7 +340,7 @@ protected int doUpdateRows(Serializable key, PersistentCollection collection, Se
// update the matching element
// FIXME update the associated entity key data
- updateInverseSideOfAssociationNavigation( session, entry, associationPersister.getAssociationKey(), assocEntryTuple, Action.REMOVE, assocEntryKey );
+ updateInverseSideOfAssociationNavigation( session, collection.getOwner(), entry, associationPersister.getAssociationKey(), assocEntryTuple, Action.REMOVE, assocEntryKey );
getElementGridType().nullSafeSet(
assocEntryTuple,
@@ -351,7 +352,7 @@ protected int doUpdateRows(Serializable key, PersistentCollection collection, Se
// put back entry tuple to actually apply changes to the store
associationPersister.getAssociation().put( assocEntryKey, assocEntryTuple );
- updateInverseSideOfAssociationNavigation( session, entry, associationPersister.getAssociationKey(), assocEntryTuple, Action.ADD, assocEntryKey );
+ updateInverseSideOfAssociationNavigation( session, collection.getOwner(), entry, associationPersister.getAssociationKey(), assocEntryTuple, Action.ADD, assocEntryKey );
count++;
}
@@ -518,7 +519,7 @@ public void deleteRows(PersistentCollection collection, Serializable id, Session
throw new AssertionFailure( "Deleting a collection tuple that is not present: " + "table {" + getTableName() + "} collectionKey {" + id + "} entry {" + entry + "}" );
}
// delete the tuple
- updateInverseSideOfAssociationNavigation( session, entry, associationPersister.getAssociationKey(), assocEntryTuple, Action.REMOVE, assocEntryKey );
+ updateInverseSideOfAssociationNavigation( session, collection.getOwner(), entry, associationPersister.getAssociationKey(), assocEntryTuple, Action.REMOVE, assocEntryKey );
associationPersister.getAssociation().remove( assocEntryKey );
count++;
@@ -558,7 +559,7 @@ public void insertRows(PersistentCollection collection, Serializable id, Session
if ( collection.needsInserting( entry, i, elementType ) ) {
// TODO: copy/paste from recreate()
RowKeyAndTuple associationRow = createAndPutAssociationRowForInsert( id, collection, associationPersister, session, i, entry );
- updateInverseSideOfAssociationNavigation( session, entry, associationPersister.getAssociationKey(), associationRow.tuple, Action.ADD, associationRow.key );
+ updateInverseSideOfAssociationNavigation( session, collection.getOwner(), entry, associationPersister.getAssociationKey(), associationRow.tuple, Action.ADD, associationRow.key );
collection.afterRowInsert( this, entry, i );
count++;
}
@@ -596,7 +597,7 @@ public void recreate(PersistentCollection collection, Serializable id, SessionIm
if ( collection.entryExists( entry, i ) ) {
// TODO: copy/paste from insertRows()
RowKeyAndTuple keyAndTuple = createAndPutAssociationRowForInsert( id, collection, associationPersister, session, i, entry );
- updateInverseSideOfAssociationNavigation( session, entry, associationPersister.getAssociationKey(), keyAndTuple.tuple, Action.ADD, keyAndTuple.key );
+ updateInverseSideOfAssociationNavigation( session, collection.getOwner(), entry, associationPersister.getAssociationKey(), keyAndTuple.tuple, Action.ADD, keyAndTuple.key );
collection.afterRowInsert( this, entry, i );
count++;
}
@@ -619,7 +620,7 @@ public void recreate(PersistentCollection collection, Serializable id, SessionIm
}
@SuppressWarnings("unchecked")
- private void updateInverseSideOfAssociationNavigation(SessionImplementor session, Object entity, AssociationKey associationKey, Tuple associationRow, Action action, RowKey rowKey) {
+ private void updateInverseSideOfAssociationNavigation(SessionImplementor session, Object associationOwner, Object entity, AssociationKey associationKey, Tuple associationRow, Action action, RowKey rowKey) {
if ( associationType == AssociationType.EMBEDDED_FK_TO_ENTITY ) {
// update the associated object
Serializable entityId = (Serializable) gridTypeOfAssociatedId.nullSafeGet( associationRow, getElementColumnNames(), session, null );
@@ -661,7 +662,7 @@ else if ( associationType == AssociationType.ASSOCIATION_TABLE_TO_ENTITY ) {
entity = session.getPersistenceContext().getEntity( session.generateEntityKey( entityId, getElementPersister() ) );
}
- AssociationPersister associationPersister = inverseCollectionPersister.getAssociationPersister( entity, elementColumnValues, session );
+ AssociationPersister associationPersister = inverseCollectionPersister.getInverseAssociationPersister( associationOwner, entity, elementColumnValues, session );
// TODO what happens when a row should be *updated* ?: I suspect ADD works OK as it's a put()
if ( action == Action.ADD ) {
@@ -735,6 +736,7 @@ public void remove(Serializable id, SessionImplementor session) throws Hibernate
// we unfortunately cannot mass change the update of the associated entity
updateInverseSideOfAssociationNavigation(
session,
+ owner,
entity,
associationPersister.getAssociationKey(),
associationRow,
@@ -873,11 +875,12 @@ public AssociationTypeContext getAssociationTypeContext(String mainSidePropertyN
return associationTypeContext;
}
- private AssociationPersister getAssociationPersister(Object collectionOwner, Serializable id, SessionImplementor session) {
+ private AssociationPersister getAssociationPersister(Object associationOwner, Serializable id, SessionImplementor session) {
return new AssociationPersister(
getOwnerEntityPersister().getMappedClass()
)
- .hostingEntity( collectionOwner )
+ .hostingEntity( associationOwner )
+ .associationOwner( associationOwner )
.gridDialect( gridDialect )
.key( id, getKeyGridType() )
.associationKeyMetadata( associationKeyMetadata )
@@ -885,11 +888,13 @@ private AssociationPersister getAssociationPersister(Object collectionOwner, Ser
.session( session );
}
- private AssociationPersister getAssociationPersister(Object collectionOwner, Object[] keyColumnValues, SessionImplementor session) {
+ private AssociationPersister getInverseAssociationPersister(Object associationOwner, Object collectionOwner, Object[] keyColumnValues, SessionImplementor session) {
return new AssociationPersister(
getOwnerEntityPersister().getMappedClass()
)
.hostingEntity( collectionOwner )
+ .associationOwner( associationOwner )
+ .inverse()
.gridDialect( gridDialect )
.keyColumnValues( keyColumnValues )
.associationKeyMetadata( associationKeyMetadata )
diff --git a/core/src/main/java/org/hibernate/ogm/persister/impl/OgmEntityPersister.java b/core/src/main/java/org/hibernate/ogm/persister/impl/OgmEntityPersister.java
index 467335b..72986ba 100644
--- a/core/src/main/java/org/hibernate/ogm/persister/impl/OgmEntityPersister.java
+++ b/core/src/main/java/org/hibernate/ogm/persister/impl/OgmEntityPersister.java
@@ -750,15 +750,18 @@ public Object loadByUniqueKey(
getPropertyNames()[propertyIndex]
);
+ Object associationOwner = session.getPersistenceContext().getEntity( new org.hibernate.engine.spi.EntityKey( (Serializable) uniqueKey, inversePersister ) );
+
AssociationPersister associationPersister = new AssociationPersister(
inversePersister.getMappedClass()
)
+ .associationOwner( associationOwner )
.gridDialect( gridDialect )
.key( uniqueKey, gridUniqueKeyType )
.associationKeyMetadata( associationKeyMetadata )
.session( session )
.associationTypeContext( associationTypeContext )
- .hostingEntity( session.getPersistenceContext().getEntity( new org.hibernate.engine.spi.EntityKey( (Serializable) uniqueKey, inversePersister ) ) );
+ .hostingEntity( associationOwner );
final Association ids = associationPersister.getAssociationOrNull();
@@ -1180,7 +1183,7 @@ else if ( isAllOrDirtyOptLocking() && oldFields != null ) {
saveSharedTuple( object, resultset, session );
if ( mightRequireInverseAssociationManagement ) {
- removeFromInverseAssociations( resultset, j, id, session );
+ removeFromInverseAssociations( object, resultset, j, id, session );
}
dehydrate( resultset, fields, propsToUpdate, j, id, session );
@@ -1216,7 +1219,7 @@ else if ( isAllOrDirtyOptLocking() && oldFields != null ) {
}
if ( mightRequireInverseAssociationManagement ) {
- addToInverseAssociations( resultset, j, id, session );
+ addToInverseAssociations( object, resultset, j, id, session );
}
}
}
@@ -1285,11 +1288,13 @@ private void dehydrate(
* Removes the given entity from the inverse associations it manages.
*/
private void removeFromInverseAssociations(
+ Object associationOwner,
Tuple resultset,
int tableIndex,
Serializable id,
SessionImplementor session) {
new EntityAssociationUpdater( this )
+ .associationOwner( associationOwner )
.id( id )
.resultset( resultset )
.session( session )
@@ -1302,11 +1307,13 @@ private void removeFromInverseAssociations(
* Adds the given entity to the inverse associations it manages.
*/
private void addToInverseAssociations(
+ Object associationOwner,
Tuple resultset,
int tableIndex,
Serializable id,
SessionImplementor session) {
new EntityAssociationUpdater( this )
+ .associationOwner( associationOwner )
.id( id )
.resultset( resultset )
.session( session )
@@ -1344,7 +1351,7 @@ public Serializable insert(Object[] fields, Object object, SessionImplementor se
dehydrate( tuple, fields, propertiesToInsert, 0, null, session );
identityColumnAwareGridDialect.insertTuple( entityKeyMetadata, tuple, getTupleContext( session ) );
Serializable id = (Serializable) getGridIdentifierType().hydrate( tuple, getIdentifierColumnNames(), session, object );
- addToInverseAssociations( tuple, 0, id, session );
+ addToInverseAssociations( object, tuple, 0, id, session );
if ( id == null ) {
throw new HibernateException( "Dialect failed to generate id for entity type " + entityKeyMetadata );
@@ -1421,7 +1428,7 @@ public void insert(Serializable id, Object[] fields, Object object, SessionImple
throw log.mustNotInsertSameEntityTwice( MessageHelper.infoString( this, id, getFactory() ), taee );
}
- addToInverseAssociations( resultset, 0, id, session );
+ addToInverseAssociations( object, resultset, 0, id, session );
}
}
@@ -1492,6 +1499,7 @@ public void delete(Serializable id, Object version, Object object, SessionImplem
//needs to be executed before the tuple removal because the AtomicMap in ISPN is cleared upon removal
if ( mightRequireInverseAssociationManagement ) {
new EntityAssociationUpdater( this )
+ .associationOwner( object )
.id( id )
.resultset( currentState )
.session( session )
diff --git a/core/src/main/java/org/hibernate/ogm/util/impl/AssociationPersister.java b/core/src/main/java/org/hibernate/ogm/util/impl/AssociationPersister.java
index af5a2dd..b255167 100644
--- a/core/src/main/java/org/hibernate/ogm/util/impl/AssociationPersister.java
+++ b/core/src/main/java/org/hibernate/ogm/util/impl/AssociationPersister.java
@@ -38,6 +38,7 @@
*
* @author Emmanuel Bernard
* @author Gunnar Morling
+ * @author Guillaume Smet
*/
public class AssociationPersister {
private GridType keyGridType;
@@ -60,6 +61,16 @@
private Boolean hostingEntityRequiresReadAfterUpdate;
private EntityPersister hostingEntityPersister;
+ /**
+ * The association owner.
+ */
+ private Object associationOwner;
+
+ /**
+ * Indicates if it is an inverse association.
+ */
+ private boolean inverse = false;
+
public AssociationPersister(Class<?> entityType) {
this.hostingEntityType = entityType;
}
@@ -94,6 +105,16 @@ public AssociationPersister hostingEntity(Object entity) {
return this;
}
+ public AssociationPersister associationOwner(Object associationOwner) {
+ this.associationOwner = associationOwner;
+ return this;
+ }
+
+ public AssociationPersister inverse() {
+ this.inverse = true;
+ return this;
+ }
+
public AssociationPersister associationTypeContext(AssociationTypeContext associationTypeContext) {
this.associationTypeContext = associationTypeContext;
return this;
@@ -147,9 +168,9 @@ public Association getAssociation() {
AssociationKey key = getAssociationKey();
if ( hostingEntity != null ) {
- OgmEntityEntryState entryState = OgmEntityEntryState.getStateFor( session, hostingEntity );
- if ( entryState.hasAssociation( associationKeyMetadata.getCollectionRole() ) ) {
- association = entryState.getAssociation( associationKeyMetadata.getCollectionRole() );
+ OgmEntityEntryState entryState = OgmEntityEntryState.getStateFor( session, associationOwner );
+ if ( hasAssociationFromState( entryState, associationKeyMetadata.getCollectionRole() ) ) {
+ association = getAssociationFromState( entryState, associationKeyMetadata.getCollectionRole() );
}
else {
association = gridDialect.getAssociation( key, getAssociationContext() );
@@ -163,8 +184,7 @@ public Association getAssociation() {
association = gridDialect.createAssociation( key, getAssociationContext() );
}
if ( hostingEntity != null ) {
- OgmEntityEntryState.getStateFor( session, hostingEntity )
- .setAssociation( associationKeyMetadata.getCollectionRole(), association );
+ setAssociationFromState( OgmEntityEntryState.getStateFor( session, associationOwner ), associationKeyMetadata.getCollectionRole(), association );
}
}
return association;
@@ -176,9 +196,9 @@ public Association getAssociation() {
public Association getAssociationOrNull() {
if ( association == null ) {
if ( hostingEntity != null ) {
- OgmEntityEntryState entryState = OgmEntityEntryState.getStateFor( session, hostingEntity );
- if ( entryState.hasAssociation( associationKeyMetadata.getCollectionRole() ) ) {
- association = entryState.getAssociation( associationKeyMetadata.getCollectionRole() );
+ OgmEntityEntryState entryState = OgmEntityEntryState.getStateFor( session, associationOwner );
+ if ( hasAssociationFromState( entryState, associationKeyMetadata.getCollectionRole() ) ) {
+ association = getAssociationFromState( entryState, associationKeyMetadata.getCollectionRole() );
return association;
}
}
@@ -186,8 +206,7 @@ public Association getAssociationOrNull() {
if ( association == null ) {
association = gridDialect.getAssociation( getAssociationKey(), getAssociationContext() );
if ( hostingEntity != null ) {
- OgmEntityEntryState.getStateFor( session, hostingEntity )
- .setAssociation( associationKeyMetadata.getCollectionRole(), association );
+ setAssociationFromState( OgmEntityEntryState.getStateFor( session, associationOwner ), associationKeyMetadata.getCollectionRole(), association );
}
}
}
@@ -201,7 +220,7 @@ public void flushToDatastore() {
if ( getAssociation().isEmpty() ) {
gridDialect.removeAssociation( getAssociationKey(), getAssociationContext() );
association = null;
- OgmEntityEntryState.getStateFor( session, hostingEntity ).setAssociation( associationKeyMetadata.getCollectionRole(), null );
+ setAssociationFromState( OgmEntityEntryState.getStateFor( session, associationOwner ), associationKeyMetadata.getCollectionRole(), null );
}
else if ( !getAssociation().getOperations().isEmpty() ) {
gridDialect.insertOrUpdateAssociation( getAssociationKey(), getAssociation(), getAssociationContext() );
@@ -258,6 +277,33 @@ private OgmEntityPersister getHostingEntityPersister() {
return (OgmEntityPersister) hostingEntityPersister;
}
+ private boolean hasAssociationFromState(OgmEntityEntryState state, String collectionRole) {
+ if ( inverse ) {
+ return state.hasInverseAssociation( collectionRole );
+ }
+ else {
+ return state.hasAssociation( collectionRole );
+ }
+ }
+
+ private Association getAssociationFromState(OgmEntityEntryState state, String collectionRole) {
+ if ( inverse ) {
+ return state.getInverseAssociation( collectionRole );
+ }
+ else {
+ return state.getAssociation( collectionRole );
+ }
+ }
+
+ private void setAssociationFromState(OgmEntityEntryState state, String collectionRole, Association association) {
+ if ( inverse ) {
+ state.setInverseAssociation( collectionRole, association );
+ }
+ else {
+ state.setAssociation( collectionRole, association );
+ }
+ }
+
/**
* Returns an {@link AssociationContext} to be passed to {@link GridDialect} operations targeting the association
* managed by this persister.
@@ -266,7 +312,7 @@ public AssociationContext getAssociationContext() {
if ( associationContext == null ) {
associationContext = new AssociationContextImpl(
associationTypeContext,
- hostingEntity != null ? OgmEntityEntryState.getStateFor( session, hostingEntity ).getTuplePointer() : new TuplePointer(),
+ hostingEntity != null ? OgmEntityEntryState.getStateFor( session, associationOwner ).getTuplePointer() : new TuplePointer(),
transactionContext( session )
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment