DATADOC-48 using the entity lifecycle for transaction synch registration

This commit is contained in:
Thomas Risberg
2011-03-29 21:17:07 -04:00
parent bfb3db5a2e
commit 057eeb101c
2 changed files with 82 additions and 28 deletions

View File

@@ -44,11 +44,8 @@ public aspect MongoDocumentBacking {
// ITD to introduce N state to Annotated objects
declare parents : (@Entity *) implements DocumentBacked;
// declare @type: DocumentBacked+: @Configurable;
// The annotated fields that will be persisted in MongoDB rather than with JPA
declare @field: @Document * (@Entity+ *).*:@Transient;
declare @field: ChangeSet (DocumentBacked+).*:@Transient;
declare @field: ChangeSetPersister (DocumentBacked+).*:@Transient;
// -------------------------------------------------------------------------
// Advise user-defined constructors of ChangeSetBacked objects to create a new
@@ -86,27 +83,44 @@ public aspect MongoDocumentBacking {
LOGGER
.debug("User-defined constructor called on DocumentBacked object of class "
+ entity.getClass());
// Populate all ITD fields
entity.setChangeSet(new HashMapChangeSet());
entity.itdChangeSetPersister = changeSetPersister;
// Populate all properties
ChangeSet changeSet = new HashMapChangeSet();
// changeSetManager.populateChangeSet(changeSet, entity);
entity.setChangeSet(changeSet);
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
throw new InvalidDataAccessResourceUsageException(
"No transaction synchronization is active");
entity.itdTransactionSynchronization =
new DocumentBackedTransactionSynchronization(changeSetPersister, 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());
}
TransactionSynchronizationManager.registerSynchronization(entity.itdTransactionSynchronization);
}
else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Transaction synchronization already active for " + entity.getClass());
}
}
}
else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Transaction syncronization is not active for " + entity.getClass());
}
}
TransactionSynchronizationManager
.registerSynchronization(new DocumentBackedTransactionSynchronization(
changeSetPersister, entity));
}
// -------------------------------------------------------------------------
// ChangeSet-related mixins
// -------------------------------------------------------------------------
// Introduced field
private ChangeSet DocumentBacked.changeSet;
@Transient private ChangeSet DocumentBacked.changeSet;
private ChangeSetPersister<?> DocumentBacked.itdChangeSetPersister;
@Transient private ChangeSetPersister<?> DocumentBacked.itdChangeSetPersister;
@Transient private DocumentBackedTransactionSynchronization DocumentBacked.itdTransactionSynchronization;
public void DocumentBacked.setChangeSet(ChangeSet cs) {
this.changeSet = cs;
@@ -126,6 +140,32 @@ public aspect MongoDocumentBacking {
return itdChangeSetPersister.getPersistentId(this, this.changeSet);
}
// lifecycle methods
@javax.persistence.PrePersist public void DocumentBacked.prePersist() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle called PrePersist: " + this.getClass().getName() + " :: " + this.get_persistent_id());
}
registerTransactionSynchronization(this);
}
@javax.persistence.PreUpdate public void DocumentBacked.preUpdate() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle called PreUpdate: " + this.getClass().getName() + " :: " + this.get_persistent_id());
}
registerTransactionSynchronization(this);
}
@javax.persistence.PreRemove public void DocumentBacked.preRemove() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle called PreRemove: " + this.getClass().getName() + " :: " + this.get_persistent_id());
}
registerTransactionSynchronization(this);
}
@javax.persistence.PostLoad public void DocumentBacked.postLoad() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle called PostLoad: " + this.getClass().getName() + " :: " + this.get_persistent_id());
}
registerTransactionSynchronization(this);
}
/**
* delegates field reads to the state accessors instance
*/
@@ -160,18 +200,6 @@ public aspect MongoDocumentBacking {
return proceed(entity, newVal);
}
// /**
// * delegates field writes to the state accessors instance
// */
// Object around(DocumentBacked entity, Object newVal) : entityIdSet(entity, newVal) {
// Field f = field(thisJoinPoint);
// String propName = f.getName();
// LOGGER.trace("SET @Id -> ChangeSet @Id property [" + propName
// + "] with value=[" + newVal + "]");
// entity.getChangeSet().set("_id", newVal);
// return proceed(entity, newVal);
// }
Field field(JoinPoint joinPoint) {
FieldSignature fieldSignature = (FieldSignature) joinPoint.getSignature();
return fieldSignature.getField();

View File

@@ -13,7 +13,11 @@ import org.springframework.persistence.document.test.Resume;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import com.mongodb.DBCollection;
import com.mongodb.Mongo;
@@ -30,6 +34,9 @@ public class CrossStoreMongoTests {
private EntityManager entityManager;
@Autowired
private PlatformTransactionManager transactionManager;
@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
@@ -71,6 +78,7 @@ public class CrossStoreMongoTests {
Assert.assertEquals("DiMark, DBA, 1990-2000" + "; "
+ "VMware, Developer, 2007-", found.getResume().getJobs());
found.getResume().addJob("SpringDeveloper.com, Consultant, 2005-2006");
found.setAge(44);
}
@Test
@@ -87,4 +95,22 @@ public class CrossStoreMongoTests {
+ "VMware, Developer, 2007-" + "; "
+ "SpringDeveloper.com, Consultant, 2005-2006", found.getResume().getJobs());
}
@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<Object>() {
public Object doInTransaction(TransactionStatus status) {
entityManager.merge(found);
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
}
}