From f9f26e7668c7f60ec818b434f633b4a7b572bba5 Mon Sep 17 00:00:00 2001 From: Thomas Risberg Date: Thu, 31 Mar 2011 13:43:13 -0400 Subject: [PATCH] DATADOC-48 added advice for moving changeset from detached entity to newly merged persistent entity --- .../mongo/MongoChangeSetPersister.java | 5 ++ .../document/mongo/MongoDocumentBacking.aj | 70 ++++++++++++++----- .../persistence/CrossStoreMongoTests.java | 59 ++++++++++++---- .../document/persistence}/test/Person.java | 2 +- .../document/persistence}/test/Resume.java | 10 ++- .../test/resources/META-INF/persistence.xml | 2 +- 6 files changed, 114 insertions(+), 34 deletions(-) rename spring-data-mongodb-cross-store/src/test/java/org/springframework/{persistence/document => data/document/persistence}/test/Person.java (94%) rename spring-data-mongodb-cross-store/src/test/java/org/springframework/{persistence/document => data/document/persistence}/test/Resume.java (60%) diff --git a/spring-data-mongodb-cross-store/src/main/java/org/springframework/data/persistence/document/mongo/MongoChangeSetPersister.java b/spring-data-mongodb-cross-store/src/main/java/org/springframework/data/persistence/document/mongo/MongoChangeSetPersister.java index 8207052c6..2ea121e0f 100644 --- a/spring-data-mongodb-cross-store/src/main/java/org/springframework/data/persistence/document/mongo/MongoChangeSetPersister.java +++ b/spring-data-mongodb-cross-store/src/main/java/org/springframework/data/persistence/document/mongo/MongoChangeSetPersister.java @@ -102,6 +102,11 @@ public class MongoChangeSetPersister implements ChangeSetPersister { @Override public Object persistState(ChangeSetBacked entity, ChangeSet cs) throws DataAccessException { + if (cs == null) { + log.debug("Flush: changeset was null, nothing to flush."); + return 0L; + } + log.debug("Flush: changeset: " + cs.getValues().keySet()); String collName = getCollectionNameForEntity(entity.getClass()); diff --git a/spring-data-mongodb-cross-store/src/main/java/org/springframework/data/persistence/document/mongo/MongoDocumentBacking.aj b/spring-data-mongodb-cross-store/src/main/java/org/springframework/data/persistence/document/mongo/MongoDocumentBacking.aj index 41dd4ca12..e587541a2 100644 --- a/spring-data-mongodb-cross-store/src/main/java/org/springframework/data/persistence/document/mongo/MongoDocumentBacking.aj +++ b/spring-data-mongodb-cross-store/src/main/java/org/springframework/data/persistence/document/mongo/MongoDocumentBacking.aj @@ -1,17 +1,17 @@ package org.springframework.data.persistence.document.mongo; import java.lang.reflect.Field; +import java.util.Map; +import javax.persistence.EntityManager; import javax.persistence.Transient; import javax.persistence.Entity; -import javax.persistence.Id; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.FieldSignature; -import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.data.document.mongodb.mapping.Document; import org.springframework.data.persistence.document.DocumentBacked; @@ -73,11 +73,37 @@ public aspect MongoDocumentBacking { args(newVal) && !set(* DocumentBacked.*); -// protected pointcut entityIdSet(DocumentBacked entity, Object newVal) : -// set(@Id * DocumentBacked+.*) && -// this(entity) && -// args(newVal) && -// !set(* DocumentBacked.*); + // intercept EntityManager.merge calls + public pointcut entityManagerMerge(EntityManager em, Object entity) : + call(* EntityManager.merge(Object)) && + target(em) && + args(entity); + + // intercept EntityManager.remove calls + public pointcut entityManagerRemove(EntityManager em, Object entity) : + call(* EntityManager.remove(Object)) && + target(em) && + args(entity); + + // move changeSet from detached entity to the newly merged persistent object + Object around(EntityManager em, Object entity) : entityManagerMerge(em, entity) { + Object mergedEntity = proceed(em, entity); + if (entity instanceof DocumentBacked && mergedEntity instanceof DocumentBacked) { + ((DocumentBacked)mergedEntity).changeSet = ((DocumentBacked)entity).getChangeSet(); + } + return mergedEntity; + } + + // clear changeSet from removed entity + Object around(EntityManager em, Object entity) : entityManagerRemove(em, entity) { + if (entity instanceof DocumentBacked) { + Map cs = ((DocumentBacked)entity).getChangeSet().getValues(); + for (String key : cs.keySet()) { + cs.put(key, null); + } + } + return proceed(em, entity); + } before(DocumentBacked entity) : arbitraryUserConstructorOfChangeSetBackedObject(entity) { LOGGER @@ -88,26 +114,26 @@ public aspect MongoDocumentBacking { entity.itdChangeSetPersister = changeSetPersister; entity.itdTransactionSynchronization = new DocumentBackedTransactionSynchronization(changeSetPersister, entity); - registerTransactionSynchronization(entity); + //registerTransactionSynchronization(entity); } private static void registerTransactionSynchronization(DocumentBacked entity) { if (TransactionSynchronizationManager.isSynchronizationActive()) { if (!TransactionSynchronizationManager.getSynchronizations().contains(entity.itdTransactionSynchronization)) { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Adding transaction synchronization for " + entity.getClass()); + LOGGER.debug("Adding transaction synchronization for " + entity); } TransactionSynchronizationManager.registerSynchronization(entity.itdTransactionSynchronization); } else { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Transaction synchronization already active for " + entity.getClass()); + LOGGER.debug("Transaction synchronization already active for " + entity); } } } else { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Transaction syncronization is not active for " + entity.getClass()); + LOGGER.debug("Transaction synchronization is not active for " + entity); } } } @@ -141,27 +167,33 @@ public aspect MongoDocumentBacking { } // lifecycle methods - @javax.persistence.PrePersist public void DocumentBacked.prePersist() { + @javax.persistence.PostPersist public void DocumentBacked.itdPostPersist() { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("JPA lifecycle called PrePersist: " + this.getClass().getName() + " :: " + this.get_persistent_id()); + LOGGER.debug("JPA lifecycle event PrePersist: " + this.getClass().getName()); } registerTransactionSynchronization(this); } - @javax.persistence.PreUpdate public void DocumentBacked.preUpdate() { + @javax.persistence.PreUpdate public void DocumentBacked.itdPreUpdate() { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("JPA lifecycle called PreUpdate: " + this.getClass().getName() + " :: " + this.get_persistent_id()); + LOGGER.debug("JPA lifecycle event PreUpdate: " + this.getClass().getName() + " :: " + this); } registerTransactionSynchronization(this); } - @javax.persistence.PreRemove public void DocumentBacked.preRemove() { + @javax.persistence.PostUpdate public void DocumentBacked.itdPostUpdate() { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("JPA lifecycle called PreRemove: " + this.getClass().getName() + " :: " + this.get_persistent_id()); + LOGGER.debug("JPA lifecycle event PostUpdate: " + this.getClass().getName() + " :: " + this); } registerTransactionSynchronization(this); } - @javax.persistence.PostLoad public void DocumentBacked.postLoad() { + @javax.persistence.PostRemove public void DocumentBacked.itdPostRemove() { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("JPA lifecycle called PostLoad: " + this.getClass().getName() + " :: " + this.get_persistent_id()); + LOGGER.debug("JPA lifecycle event PostRemove: " + this.getClass().getName() + " :: " + this); + } + registerTransactionSynchronization(this); + } + @javax.persistence.PostLoad public void DocumentBacked.itdPostLoad() { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("JPA lifecycle event PostLoad: " + this.getClass().getName() + " :: " + this); } registerTransactionSynchronization(this); } diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java index b52a4f17e..dbfbc3f0f 100644 --- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java +++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java @@ -8,8 +8,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.document.mongodb.MongoTemplate; -import org.springframework.persistence.document.test.Person; -import org.springframework.persistence.document.test.Resume; +import org.springframework.data.document.persistence.test.Person; +import org.springframework.data.document.persistence.test.Resume; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -99,18 +99,53 @@ public class CrossStoreMongoTests { @Test public void testMergeJpaEntityWithMongoDocument() { TransactionTemplate txTemplate = new TransactionTemplate(transactionManager); - final Person found = entityManager.find(Person.class, 1L); - found.setAge(77); - found.getResume().addJob("TargetRx, Developer, 2000-2005"); - txTemplate.execute(new TransactionCallback() { - public Object doInTransaction(TransactionStatus status) { - entityManager.merge(found); + final Person detached = entityManager.find(Person.class, 1L); + detached.getResume().addJob("TargetRx, Developer, 2000-2005"); + Person merged = txTemplate.execute(new TransactionCallback() { + public Person doInTransaction(TransactionStatus status) { + return entityManager.merge(detached); + } + }); + Assert.assertTrue(detached.getResume().getJobs().contains("TargetRx, Developer, 2000-2005")); + Assert.assertTrue(merged.getResume().getJobs().contains("TargetRx, Developer, 2000-2005")); + final Person updated = entityManager.find(Person.class, 1L); + Assert.assertTrue(updated.getResume().getJobs().contains("TargetRx, Developer, 2000-2005")); + } + + @Test + public void testRemoveJpaEntityWithMongoDocument() { + TransactionTemplate txTemplate = new TransactionTemplate(transactionManager); + txTemplate.execute(new TransactionCallback() { + public Person doInTransaction(TransactionStatus status) { + Person p2 = new Person("Thomas", 20); + Resume r2 = new Resume(); + r2.addEducation("Skanstulls High School, 1975"); + r2.addJob("DiMark, DBA, 1990-2000"); + p2.setResume(r2); + p2.setId(2L); + entityManager.persist(p2); + Person p3 = new Person("Thomas", 20); + Resume r3 = new Resume(); + r3.addEducation("Univ. of Stockholm, 1980"); + r3.addJob("VMware, Developer, 2007-"); + p3.setResume(r3); + p3.setId(3L); + entityManager.persist(p3); return null; } }); - final Person updated = entityManager.find(Person.class, 1L); - // assert that the new values are in respective DBs - // TODO: during merge we lose the changeset since JPA creates a new persistent instance - - // we need to move the existing changeset over somehow + txTemplate.execute(new TransactionCallback() { + public Person doInTransaction(TransactionStatus status) { + final Person found2 = entityManager.find(Person.class, 2L); + final Person found3 = entityManager.find(Person.class, 3L); + entityManager.remove(found2); + return null; + } + }); + final Person found2 = entityManager.find(Person.class, 2L); + final Person found3 = entityManager.find(Person.class, 3L); +// TODO: assert that any documents for Person 2 are gone +// System.out.println(found2); +// System.out.println(found3); } } diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Person.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/test/Person.java similarity index 94% rename from spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Person.java rename to spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/test/Person.java index 9f4869241..3f84fdbd1 100644 --- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Person.java +++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/test/Person.java @@ -1,4 +1,4 @@ -package org.springframework.persistence.document.test; +package org.springframework.data.document.persistence.test; import javax.persistence.Entity; import javax.persistence.Id; diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Resume.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/test/Resume.java similarity index 60% rename from spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Resume.java rename to spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/test/Resume.java index adfdfef64..85ad9ac95 100644 --- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Resume.java +++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/test/Resume.java @@ -1,8 +1,14 @@ -package org.springframework.persistence.document.test; +package org.springframework.data.document.persistence.test; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.data.persistence.document.mongo.MongoDocumentBacking; //@DocumentEntity public class Resume { + private static final Log LOGGER = LogFactory.getLog(Resume.class); + private String education = ""; private String jobs = ""; @@ -12,6 +18,7 @@ public class Resume { } public void addEducation(String education) { + LOGGER.debug("Adding education " + education); this.education = this.education + (this.education.length() > 0 ? "; " : "") + education; } @@ -20,6 +27,7 @@ public class Resume { } public void addJob(String job) { + LOGGER.debug("Adding job " + job); this.jobs = this.jobs + (this.jobs.length() > 0 ? "; " : "") + job; } diff --git a/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml b/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml index 9faefc962..60ff84633 100644 --- a/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml +++ b/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> org.hibernate.ejb.HibernatePersistence - org.springframework.persistence.document.test.Person + org.springframework.data.document.persistence.test.Person