From c0b8a8e227a46020082aa7735260bbc8041f361e Mon Sep 17 00:00:00 2001 From: Mark Pollack Date: Tue, 29 Mar 2011 18:01:10 -0400 Subject: [PATCH 1/3] DATADOC-74 Update to version 2.4 of mongo driver --- spring-data-mongodb/pom.xml | 2 +- .../mongodb/mapping/MappingTests.java | 43 +++++++++++++------ 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index a1980c35b..f2ad6ff58 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -13,7 +13,7 @@ Spring Data MongoDB Support - 2.3 + 2.4 diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingTests.java index 2ad2f8eb2..ae21e8324 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingTests.java @@ -24,37 +24,51 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.Before; import org.junit.Test; +import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.data.document.mongodb.MongoDbUtils; import org.springframework.data.document.mongodb.MongoTemplate; import org.springframework.data.document.mongodb.query.Criteria; import org.springframework.data.document.mongodb.query.Query; +import com.mongodb.Mongo; + /** * @author Jon Brisbin */ public class MappingTests { + private static final Log LOGGER = LogFactory.getLog(MongoDbUtils.class); + ApplicationContext applicationContext; MongoTemplate template; MongoMappingContext mappingContext; @Before - public void setUp() { + public void setUp() throws Exception { + Mongo mongo = new Mongo(); + mongo.getDB("database").getCollection("person").drop(); applicationContext = new ClassPathXmlApplicationContext("/mapping.xml"); template = applicationContext.getBean(MongoTemplate.class); mappingContext = applicationContext.getBean(MongoMappingContext.class); } @Test - public void testPersonPojo() { + public void testPersonPojo() throws Exception { + LOGGER.info("about to create new personpojo"); PersonPojo p = new PersonPojo(12345, "Person", "Pojo"); + LOGGER.info("about to insert"); template.insert(p); + LOGGER.info("done inserting"); assertNotNull(p.getId()); - List result = template.find(new Query(Criteria.where("ssn").is(12345)), PersonPojo.class); + List result = template.find( + new Query(Criteria.where("ssn").is(12345)), PersonPojo.class); assertThat(result.size(), is(1)); assertThat(result.get(0).getSsn(), is(12345)); } @@ -64,7 +78,8 @@ public class MappingTests { PersonCustomIdName p = new PersonCustomIdName(123456, "Custom", "Id"); template.insert(p); - List result = template.find(new Query(Criteria.where("ssn").is(123456)), PersonCustomIdName.class); + List result = template.find( + new Query(Criteria.where("ssn").is(123456)), PersonCustomIdName.class); assertThat(result.size(), is(1)); assertNotNull(result.get(0).getCustomId()); } @@ -84,18 +99,20 @@ public class MappingTests { template.insert(p); assertNotNull(p.getId()); - List result = template.find(new Query(Criteria.where("ssn").is(1234567)), PersonMapProperty.class); + List result = template.find( + new Query(Criteria.where("ssn").is(1234567)), PersonMapProperty.class); assertThat(result.size(), is(1)); assertThat(result.get(0).getAccounts().size(), is(2)); - assertThat(result.get(0).getAccounts().get("checking").getBalance(), is(1000.0f)); + assertThat(result.get(0).getAccounts().get("checking").getBalance(), + is(1000.0f)); } @Test - @SuppressWarnings({"unchecked"}) + @SuppressWarnings({ "unchecked" }) public void testWriteEntity() { Address addr = new Address(); - addr.setLines(new String[]{"1234 W. 1st Street", "Apt. 12"}); + addr.setLines(new String[] { "1234 W. 1st Street", "Apt. 12" }); addr.setCity("Anytown"); addr.setPostalCode(12345); addr.setCountry("USA"); @@ -120,7 +137,8 @@ public class MappingTests { assertNotNull(p.getId()); - List result = template.find(new Query(Criteria.where("ssn").is(123456789)), Person.class); + List result = template.find( + new Query(Criteria.where("ssn").is(123456789)), Person.class); assertThat(result.size(), is(1)); assertThat(result.get(0).getAddress().getCountry(), is("USA")); assertThat(result.get(0).getAccounts(), notNullValue()); @@ -129,7 +147,7 @@ public class MappingTests { @Test public void testUniqueIndex() { Address addr = new Address(); - addr.setLines(new String[]{"1234 W. 1st Street", "Apt. 12"}); + addr.setLines(new String[] { "1234 W. 1st Street", "Apt. 12" }); addr.setCity("Anytown"); addr.setPostalCode(12345); addr.setCountry("USA"); @@ -140,12 +158,13 @@ public class MappingTests { template.insert("person", p1); template.insert("person", p2); - List result = template.find(new Query(Criteria.where("ssn").is(1234567890)), Person.class); + List result = template.find( + new Query(Criteria.where("ssn").is(1234567890)), Person.class); assertThat(result.size(), is(1)); } @Test - public void testEvents(){ + public void testEvents() { } From bfb3db5a2e375298538ca1d2c50dbc91c16e3082 Mon Sep 17 00:00:00 2001 From: Thomas Risberg Date: Tue, 29 Mar 2011 21:16:11 -0400 Subject: [PATCH 2/3] more tests for the chained Criteria support --- .../document/mongodb/query/QueryTests.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/query/QueryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/query/QueryTests.java index 76426e4b8..ddec63659 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/query/QueryTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/query/QueryTests.java @@ -29,13 +29,6 @@ public class QueryTests { Assert.assertEquals(expected, q.getQueryObject().toString()); } - @Test - public void testSimpleQueryWithChainedCriteria() { - Query q = new Query(where("name").is("Thomas").and("age").lt(80)); - String expected = "{ \"name\" : \"Thomas\" , \"age\" : { \"$lt\" : 80}}"; - Assert.assertEquals(expected, q.getQueryObject().toString()); - } - @Test public void testQueryWithNot() { Query q = new Query(where("name").is("Thomas")).and(where("age").not().mod(10, 0)); @@ -81,6 +74,22 @@ public class QueryTests { Assert.assertEquals(expected, q.getQueryObject().toString()); } + @Test + public void testSimpleQueryWithChainedCriteria() { + Query q = new Query(where("name").is("Thomas").and("age").lt(80)); + String expected = "{ \"name\" : \"Thomas\" , \"age\" : { \"$lt\" : 80}}"; + Assert.assertEquals(expected, q.getQueryObject().toString()); + } + + @Test + public void testComplexQueryWithMultipleChainedCriteria() { + Query q1 = new Query(where("name").regex("^T.*").and("age").gt(20).lt(80).and("city").in("Stockholm", "London", "New York")); + Query q2 = new Query(where("name").regex("^T.*").and("age").gt(20).lt(80)).and(where("city").in("Stockholm", "London", "New York")); + Assert.assertEquals(q1.getQueryObject().toString(), q2.getQueryObject().toString()); + Query q3 = new Query(where("name").regex("^T.*")).and(where("age").gt(20).lt(80)).and(where("city").in("Stockholm", "London", "New York")); + Assert.assertEquals(q1.getQueryObject().toString(), q3.getQueryObject().toString()); + } + @Test public void testQueryWithElemMatch() { Query q = new Query(where("openingHours").elemMatch(where("dayOfWeek").is("Monday").and("open").lte("1800"))); From 057eeb101c8d7b70d348b611da821c03f09e067b Mon Sep 17 00:00:00 2001 From: Thomas Risberg Date: Tue, 29 Mar 2011 21:17:07 -0400 Subject: [PATCH 3/3] DATADOC-48 using the entity lifecycle for transaction synch registration --- .../document/mongo/MongoDocumentBacking.aj | 84 ++++++++++++------- .../persistence/CrossStoreMongoTests.java | 26 ++++++ 2 files changed, 82 insertions(+), 28 deletions(-) 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 e98e8395c..6d1b29b0a 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 @@ -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(); 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 b7fc0036e..b52a4f17e 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 @@ -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() { + 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 + } }