DATADOC-48 simplified cross-store persistence of @Document annotated fields in a JPA entity

This commit is contained in:
Thomas Risberg
2011-03-23 07:44:52 -04:00
parent 74df4349ed
commit f21f25fb52
17 changed files with 548 additions and 575 deletions

View File

@@ -21,20 +21,30 @@
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-core</artifactId>
</dependency>
<dependency>
<!-- <dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-aspects</artifactId>
</dependency>
</dependency> -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
@@ -197,10 +207,10 @@
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
<aspectLibrary>
<!-- <aspectLibrary>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-aspects</artifactId>
</aspectLibrary>
</aspectLibrary> -->
</aspectLibraries>
<source>1.6</source>
<target>1.6</target>

View File

@@ -0,0 +1,7 @@
package org.springframework.data.persistence.document;
import org.springframework.data.support.ChangeSetBacked;
public interface DocumentBacked extends ChangeSetBacked {
}

View File

@@ -0,0 +1,70 @@
package org.springframework.data.persistence.document;
//public class DocumentBackedTransactionSynchronization {
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.support.ChangeSetBacked;
import org.springframework.data.support.ChangeSetPersister;
import org.springframework.transaction.support.TransactionSynchronization;
public class DocumentBackedTransactionSynchronization implements TransactionSynchronization {
protected final Log log = LogFactory.getLog(getClass());
private ChangeSetPersister<Object> changeSetPersister;
private ChangeSetBacked entity;
private int changeSetTxStatus = -1;
public DocumentBackedTransactionSynchronization(ChangeSetPersister<Object> changeSetPersister, ChangeSetBacked entity) {
this.changeSetPersister = changeSetPersister;
this.entity = entity;
}
@Override
public void afterCommit() {
log.debug("After Commit called for " + entity);
changeSetPersister.persistState(entity.getClass(), entity.getChangeSet());
changeSetTxStatus = 0;
}
@Override
public void afterCompletion(int status) {
log.debug("After Completion called with status = " + status);
if (changeSetTxStatus == 0) {
if (status == STATUS_COMMITTED) {
// this is good
log.debug("ChangedSetBackedTransactionSynchronization completed successfully for " + this.entity);
}
else {
// this could be bad - TODO: compensate
log.error("ChangedSetBackedTransactionSynchronization failed for " + this.entity);
}
}
}
@Override
public void beforeCommit(boolean readOnly) {
}
@Override
public void beforeCompletion() {
}
@Override
public void flush() {
}
@Override
public void resume() {
throw new IllegalStateException("ChangedSetBackedTransactionSynchronization does not support transaction suspension currently.");
}
@Override
public void suspend() {
throw new IllegalStateException("ChangedSetBackedTransactionSynchronization does not support transaction suspension currently.");
}
}

View File

@@ -0,0 +1,138 @@
package org.springframework.data.persistence.document.mongo;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.document.mongodb.CollectionCallback;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.support.ChangeSet;
import org.springframework.data.support.ChangeSetBacked;
import org.springframework.data.support.ChangeSetPersister;
import org.springframework.util.ClassUtils;
public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
private static final String ENTITY_CLASS = "_entity_class";
private static final String ENTITY_ID = "_entity_id";
private static final String ENTITY_FIELD_NAME = "_entity_field_name";
private static final String ENTITY_FIELD_CLASS = "_entity_field_class";
protected final Log log = LogFactory.getLog(getClass());
private MongoTemplate mongoTemplate;
public void setMongoTemplate(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
@Override
public void getPersistentState(Class<? extends ChangeSetBacked> entityClass,
Object id, final ChangeSet changeSet) throws DataAccessException,
NotFoundException {
String collName = getCollectionNameForEntity(entityClass);
final DBObject dbk = new BasicDBObject();
dbk.put(ENTITY_ID, id);
dbk.put(ENTITY_CLASS, entityClass.getName());
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
@Override
public Object doInCollection(DBCollection collection)
throws MongoException, DataAccessException {
for (DBObject dbo : collection.find(dbk)) {
String key = (String) dbo.get(ENTITY_FIELD_NAME);
String className = (String) dbo.get(ENTITY_FIELD_CLASS);
if (className == null) {
throw new DataIntegrityViolationException(
"Unble to convert property " + key
+ ": Invalid metadata, " + ENTITY_FIELD_CLASS + " not available");
}
Class<?> clazz = null;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new DataIntegrityViolationException(
"Unble to convert property " + key + " of type " + className, e);
}
Object value = mongoTemplate.getConverter().read(clazz, dbo);
changeSet.set(key, value);
}
return null;
}
});
}
@Override
public Object getPersistentId(Class<? extends ChangeSetBacked> entityClass,
ChangeSet cs) throws DataAccessException {
log.debug("getPersistentId called on " + entityClass);
if (cs == null) {
return null;
}
if (cs.getValues().get(ChangeSetPersister.ID_KEY) == null) {
// Not yet persistent
return null;
}
Object o = cs.getValues().get(ChangeSetPersister.ID_KEY);
return o;
}
@Override
public Object persistState(Class<? extends ChangeSetBacked> entityClass,
ChangeSet cs) throws DataAccessException {
log.debug("Flush: changeset: " + cs.getValues().keySet());
String collName = getCollectionNameForEntity(entityClass);
DBCollection dbc = mongoTemplate.getCollection(collName);
if (dbc == null) {
dbc = mongoTemplate.createCollection(collName);
}
for (String key : cs.getValues().keySet()) {
if (key != null && !key.startsWith("_") && !key.equals(ChangeSetPersister.ID_KEY)) {
Object value = cs.getValues().get(key);
final DBObject dbQuery = new BasicDBObject();
dbQuery.put(ENTITY_ID, cs.getValues().get(ChangeSetPersister.ID_KEY));
dbQuery.put(ENTITY_CLASS, entityClass.getName());
dbQuery.put(ENTITY_FIELD_NAME, key);
dbQuery.put(ENTITY_FIELD_CLASS, value.getClass().getName());
DBObject dbId = mongoTemplate.execute(collName,
new CollectionCallback<DBObject>() {
@Override
public DBObject doInCollection(DBCollection collection)
throws MongoException, DataAccessException {
return collection.findOne(dbQuery);
}
});
final DBObject dbDoc = new BasicDBObject();
mongoTemplate.getConverter().write(value, dbDoc);
dbDoc.putAll(dbQuery);
if (dbId != null) {
dbDoc.put("_id", dbId.get("_id"));
}
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
@Override
public Object doInCollection(DBCollection collection)
throws MongoException, DataAccessException {
collection.save(dbDoc);
return null;
}
});
}
}
return 0L;
}
private String getCollectionNameForEntity(
Class<? extends ChangeSetBacked> entityClass) {
return ClassUtils.getQualifiedName(entityClass);
}
}

View File

@@ -0,0 +1,179 @@
package org.springframework.data.persistence.document.mongo;
import java.lang.reflect.Field;
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;
import org.springframework.data.persistence.document.DocumentBackedTransactionSynchronization;
import org.springframework.data.support.ChangeSet;
import org.springframework.data.support.ChangeSetPersister;
import org.springframework.data.support.ChangeSetPersister.NotFoundException;
import org.springframework.data.support.HashMapChangeSet;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* Aspect to turn an object annotated with @Document into a persistent document
* using Mongo.
*
* @author Thomas Risberg
*/
public aspect MongoDocumentBacking {
private static final Log LOGGER = LogFactory
.getLog(MongoDocumentBacking.class);
// Aspect shared config
private ChangeSetPersister<Object> changeSetPersister;
public void setChangeSetPersister(
ChangeSetPersister<Object> changeSetPersister) {
this.changeSetPersister = changeSetPersister;
}
// ITD to introduce N state to Annotated objects
declare parents : (@Entity *) implements DocumentBacked;
// declare @type: DocumentBacked+: @Configurable;
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
// backing ChangeSet
// -------------------------------------------------------------------------
pointcut arbitraryUserConstructorOfChangeSetBackedObject(DocumentBacked entity) :
execution((DocumentBacked+).new(..)) &&
!execution((DocumentBacked+).new(ChangeSet)) &&
this(entity);
pointcut finderConstructorOfChangeSetBackedObject(DocumentBacked entity,
ChangeSet cs) :
execution((DocumentBacked+).new(ChangeSet)) &&
this(entity) &&
args(cs);
protected pointcut entityFieldGet(DocumentBacked entity) :
get(@Document * DocumentBacked+.*) &&
this(entity) &&
!get(* DocumentBacked.*);
protected pointcut entityFieldSet(DocumentBacked entity, Object newVal) :
set(@Document * DocumentBacked+.*) &&
this(entity) &&
args(newVal) &&
!set(* DocumentBacked.*);
protected pointcut entityIdSet(DocumentBacked entity, Object newVal) :
set(@Id * DocumentBacked+.*) &&
this(entity) &&
args(newVal) &&
!set(* DocumentBacked.*);
before(DocumentBacked entity) : arbitraryUserConstructorOfChangeSetBackedObject(entity) {
LOGGER
.debug("User-defined constructor called on DocumentBacked object of class "
+ entity.getClass());
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");
}
TransactionSynchronizationManager
.registerSynchronization(new DocumentBackedTransactionSynchronization(
changeSetPersister, entity));
}
// -------------------------------------------------------------------------
// ChangeSet-related mixins
// -------------------------------------------------------------------------
// Introduced field
private ChangeSet DocumentBacked.changeSet;
private ChangeSetPersister<?> DocumentBacked.itdChangeSetPersister;
public void DocumentBacked.setChangeSet(ChangeSet cs) {
this.changeSet = cs;
}
public ChangeSet DocumentBacked.getChangeSet() {
return changeSet;
}
// Flush the entity state to the persistent store
public void DocumentBacked.flush() {
itdChangeSetPersister.persistState(this.getClass(), this.changeSet);
}
public Object DocumentBacked.get_persistent_id() {
return itdChangeSetPersister.getPersistentId(this.getClass(),
this.changeSet);
}
/**
* delegates field reads to the state accessors instance
*/
Object around(DocumentBacked entity): entityFieldGet(entity) {
Field f = field(thisJoinPoint);
String propName = f.getName();
LOGGER.trace("GET " + f + " -> ChangeSet value property [" + propName
+ "] using: " + entity.getChangeSet());
if (entity.getChangeSet().getValues().get(propName) == null) {
try {
this.changeSetPersister.getPersistentState(entity.getClass(),
entity.get_persistent_id(), entity.getChangeSet());
} catch (NotFoundException e) {
}
}
Object fValue = entity.getChangeSet().getValues().get(propName);
if (fValue != null) {
return fValue;
}
return proceed(entity);
}
/**
* delegates field writes to the state accessors instance
*/
Object around(DocumentBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
Field f = field(thisJoinPoint);
String propName = f.getName();
LOGGER.trace("SET " + f + " -> ChangeSet number value property [" + propName
+ "] with value=[" + newVal + "]");
entity.getChangeSet().set(propName, newVal);
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

@@ -1,19 +0,0 @@
package org.springframework.persistence.document;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to denote an object that should be transparently persisted
* using MongoDB
*
* @author Thomas Risberg
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DocumentEntity {
}

View File

@@ -1,119 +0,0 @@
package org.springframework.persistence.document;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.persistence.support.ChangeSet;
import org.springframework.persistence.support.ChangeSetBacked;
import org.springframework.persistence.support.ChangeSetPersister;
import org.springframework.util.ClassUtils;
//import edu.emory.mathcs.backport.java.util.Arrays;
public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
protected final Log log = LogFactory.getLog(getClass());
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private ConversionService conversionService;
@Override
public void getPersistentState(Class<? extends ChangeSetBacked> entityClass, Object id, ChangeSet changeSet)
throws DataAccessException, NotFoundException {
String collection = ClassUtils.getShortName(entityClass).toLowerCase();
DBObject q = new BasicDBObject();
q.put("_id", id);
try {
DBObject dbo = mongoTemplate.getCollection(collection).findOne(q);
log.debug("Found DBObject: " + dbo);
if (dbo == null) {
throw new NotFoundException();
}
String classShortName = ClassUtils.getShortName(entityClass);
for (Object property : dbo.toMap().keySet()) {
String propertyKey = (String) property;
String propertyName = propertyKey.startsWith(classShortName) ? propertyKey.substring(propertyKey.indexOf(classShortName)
+ classShortName.length() + 1) : propertyKey;
// System.err.println("Mongo persisted property [" + propertyName + "] :: " + propertyKey + " = " + dbo.get(propertyKey));
if (propertyKey.startsWith("_")) {
// Id or class
changeSet.set(propertyKey, dbo.get(propertyKey));
} else {
//throw new IllegalStateException("Unknown property [" + propertyName + "] found in MongoDB store");
changeSet.set(propertyKey, dbo.get(propertyKey));
}
}
} catch (MongoException ex) {
throw new DataAccessResourceFailureException("Can't read from Mongo", ex);
}
}
@Override
public Object getPersistentId(Class<? extends ChangeSetBacked> entityClass,
ChangeSet cs) throws DataAccessException {
log.debug("getPersistentId called on " + entityClass);
if (cs == null) {
return null;
}
if (cs.getValues().get(ChangeSetPersister.ID_KEY) == null) {
// Not yet persistent
return null;
}
Object o = cs.getValues().get(ChangeSetPersister.ID_KEY);
return o;
}
@Override
public Object persistState(Class<? extends ChangeSetBacked> entityClass, ChangeSet cs) throws DataAccessException {
log.info("PERSIST::" + cs);
cs.set(CLASS_KEY, entityClass.getName());
String idstr = cs.get(ID_KEY, String.class, this.conversionService);
Object id = null;
if (idstr != null) {
id = idstr;
}
if (id == null) {
log.info("Flush: entity make persistent; data store will assign id");
cs.set("_class", entityClass.getName());
String collection = entityClass.getSimpleName().toLowerCase();
DBCollection dbc = mongoTemplate.getCollection(collection);
DBObject dbo = mapChangeSetToDbObject(cs);
if (dbc == null) {
dbc = mongoTemplate.createCollection(collection);
}
dbc.save(dbo);
id = dbo.get(ID_KEY);
log.info("Data store assigned id: " + id);
} else {
log.info("Flush: entity already persistent with id=" + id);
String collection = entityClass.getName();
DBCollection dbc = mongoTemplate.getCollection(collection);
DBObject dbo = mapChangeSetToDbObject(cs);
if (dbc == null) {
throw new DataAccessResourceFailureException("Expected to find a collection named '" + collection + "'. It was not found, so ChangeSet can't be persisted.");
}
dbc.save(dbo);
}
return 0L;
}
private DBObject mapChangeSetToDbObject(ChangeSet cs) {
BasicDBObject dbo = new BasicDBObject();
for (String property : cs.getValues().keySet()) {
dbo.put(property, cs.getValues().get(property));
}
return dbo;
}
}

View File

@@ -1,12 +0,0 @@
package org.springframework.persistence.document;
import org.springframework.persistence.support.AbstractDeferredUpdateMixinFields;
/**
* Aspect to turn an object annotated with DocumentEntity into a document entity using Mongo.
*
* @author Thomas Risberg
*/
public aspect MongoDocumentBacking extends AbstractDeferredUpdateMixinFields<DocumentEntity> {
}

View File

@@ -1,78 +0,0 @@
package org.springframework.persistence.document;
import java.lang.reflect.Field;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.persistence.OrderedEntityOperations;
import org.springframework.persistence.RelatedEntity;
import org.springframework.persistence.support.ChangeSet;
import org.springframework.persistence.support.ChangeSetBacked;
import org.springframework.persistence.support.ChangeSetPersister.NotFoundException;
import org.springframework.persistence.support.EntityInstantiator;
import org.springframework.persistence.support.HashMapChangeSet;
public class MongoEntityOperations extends OrderedEntityOperations<Object, ChangeSetBacked> {
@Autowired
private MongoTemplate mongoTemplate;
private EntityInstantiator<ChangeSetBacked, ChangeSet> entityInstantiator;
private MongoChangeSetPersister changeSetPersister;
public void setEntityInstantiator(EntityInstantiator<ChangeSetBacked, ChangeSet> entityInstantiator) {
this.entityInstantiator = entityInstantiator;
}
@Autowired
public void setChangeSetPersister(MongoChangeSetPersister changeSetPersister) {
this.changeSetPersister = changeSetPersister;
}
@Override
public boolean cacheInEntity() {
return true;
}
@Override
public ChangeSetBacked findEntity(Class<ChangeSetBacked> entityClass, Object key) throws DataAccessException {
try {
ChangeSet cs = new HashMapChangeSet();
changeSetPersister.getPersistentState(entityClass, key, cs);
return entityInstantiator.createEntityFromState(cs, entityClass);
} catch (NotFoundException ex) {
return null;
}
}
@Override
public Object findUniqueKey(ChangeSetBacked entity) throws DataAccessException {
return entity.getId();
}
@Override
public boolean isTransactional() {
// TODO
return false;
}
@Override
public boolean isTransient(ChangeSetBacked entity) throws DataAccessException {
return entity.getId() == null;
}
@Override
public Object makePersistent(Object owner, ChangeSetBacked entity, Field f, RelatedEntity fs) throws DataAccessException {
changeSetPersister.persistState(entity.getClass(), entity.getChangeSet());
return entity.getId();
}
@Override
public boolean supports(Class<?> entityClass, RelatedEntity fs) {
return entityClass.isAnnotationPresent(DocumentEntity.class);
}
}

View File

@@ -3,14 +3,11 @@ package org.springframework.data.document.persistence;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import com.mongodb.*;
import org.junit.Assert;
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.Account;
import org.springframework.persistence.document.test.MongoPerson;
import org.springframework.persistence.document.test.Person;
import org.springframework.persistence.document.test.Resume;
import org.springframework.test.annotation.Rollback;
@@ -18,6 +15,9 @@ import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import com.mongodb.DBCollection;
import com.mongodb.Mongo;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/META-INF/spring/applicationContext.xml")
public class CrossStoreMongoTests {
@@ -30,8 +30,6 @@ public class CrossStoreMongoTests {
private EntityManager entityManager;
private String colName = MongoPerson.class.getSimpleName().toLowerCase();
@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
@@ -45,65 +43,6 @@ public class CrossStoreMongoTests {
}
}
@Test
@Transactional
@Rollback(false)
public void testUserConstructor() {
clearData(colName);
int age = 33;
MongoPerson p = new MongoPerson("Thomas", age);
Assert.assertEquals(age, p.getAge());
p.birthday();
Assert.assertEquals(1 + age, p.getAge());
}
@Test
@Transactional
public void testInstantiatedFinder() throws MongoException {
DBCollection col = this.mongoTemplate.getCollection(colName);
DBObject dbo = col.findOne();
Object _id = dbo.get("_id");
MongoPerson found = MongoPerson.findPerson(_id);
Assert.assertNotNull(found);
Assert.assertEquals(_id, found.getId());
System.out.println("Loaded MongoPerson data: " + found);
}
@Test
@Transactional
@Rollback(false)
public void testCreateMongoToJpaEntityRelationship() {
clearData(colName);
Account a = new Account();
a.setName("My Account");
a.setFriendlyName("My Test Acct.");
a.setBalance(123.45F);
a.setId(2L);
MongoPerson p = new MongoPerson("Jack", 22);
entityManager.persist(a);
p.setAccount(a);
}
@Test
@Transactional
public void testReadMongoToJpaEntityRelationship() {
DBCollection col = this.mongoTemplate.getCollection(colName);
DBCursor dbc = col.find();
Object _id = null;
for (DBObject dbo : dbc) {
System.out.println(dbo);
if ("Jack".equals(dbo.get("name"))) {
_id = dbo.get("_id");
break;
}
}
System.out.println(_id);
MongoPerson found = MongoPerson.findPerson(_id);
System.out.println(found);
if (found != null)
System.out.println(found.getAccount());
}
@Test
@Transactional
@Rollback(false)
@@ -122,12 +61,20 @@ public class CrossStoreMongoTests {
@Test
@Transactional
@Rollback(false)
public void testReadJpaToMongoEntityRelationship() {
Person found = entityManager.find(Person.class, 1L);
System.out.println(found);
// TODO: This part isn't working yet - there is no reference to the Momgo _id stored in the db
// if (found != null)
// System.out.println(found.getResume());
}
Assert.assertNotNull(found);
// TODO: This part isn't quite working yet - need to intercept the id population from JPA EM
found.setId(found.getId());
Assert.assertEquals(Long.valueOf(1), found.getId());
Assert.assertNotNull(found);
Assert.assertEquals(Long.valueOf(1), found.getId());
Assert.assertNotNull(found.getResume());
Assert.assertEquals("DiMark, DBA, 1990-2000" + "; " + "VMware, Developer, 2007-",
found.getResume().getJobs());
found.getResume().addJob("Developer, SpringDeveloper.com, 2005-2006");
}
}

View File

@@ -1,63 +0,0 @@
package org.springframework.persistence.document.test;
import org.springframework.persistence.RelatedEntity;
import org.springframework.persistence.document.DocumentEntity;
@DocumentEntity
public class MongoPerson {
// TODO only public because of AspectJ bug
public String name;
public int age;
public java.util.Date birthDate;
// TODO only public to check weaving bug--
// seems to work whereas private didn't
@RelatedEntity
public Account account;
public MongoPerson(String name, int age) {
this.name = name;
this.age = age;
this.birthDate = new java.util.Date();
}
public void birthday() {
++age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public java.util.Date getBirthDate() {
return birthDate;
}
public void setBirthDate(java.util.Date birthDate) {
this.birthDate = birthDate;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}

View File

@@ -1,54 +0,0 @@
package org.springframework.persistence.document.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.persistence.support.*;
import org.springframework.persistence.support.ChangeSetPersister.NotFoundException;
/**
* EXAMPLE OF CODE THAT SHOULD BE GENERATED BY ROO BESIDES EACH MONGOENTITY CLASS
*
* Note: Combines X_Roo_Entity with X_Roo_Finder, as
* we need only a single aspect for entities.
*
* @author Thomas Risberg
*
*/
privileged aspect MongoPerson_Roo_Mongo_Entity {
private static ChangeSetPersister<Object> changeSetPersister() {
return new MongoConfigurationHolder().changeSetConfig.getChangeSetPersister();
}
private static ChangeSetSynchronizer<ChangeSetBacked> changeSetManager() {
return new MongoConfigurationHolder().changeSetConfig.getChangeSetManager();
}
@Configurable
public static class MongoConfigurationHolder {
@Autowired
@Qualifier("mongoChangeSetConfiguration")
public ChangeSetConfiguration<Object> changeSetConfig;
}
/**
* Add constructor that takes ChangeSet.
* @param ChangeSet
*/
public MongoPerson.new(ChangeSet cs) {
super();
setChangeSet(cs);
}
public static MongoPerson MongoPerson.findPerson(Object id) {
ChangeSet rv = new HashMapChangeSet();
try {
changeSetPersister().getPersistentState(MongoPerson.class, id, rv);
return new MongoPerson(rv);
} catch (NotFoundException ex) {
return null;
}
}
}

View File

@@ -3,7 +3,7 @@ package org.springframework.persistence.document.test;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.springframework.persistence.RelatedEntity;
import org.springframework.data.document.mongodb.mapping.Document;
@Entity
public class Person {
@@ -17,8 +17,7 @@ public class Person {
private java.util.Date birthDate;
// @Document // need to decide what the annotation here should be
@RelatedEntity
@Document
public Resume resume;
public Person() {

View File

@@ -1,8 +1,6 @@
package org.springframework.persistence.document.test;
import org.springframework.persistence.document.DocumentEntity;
@DocumentEntity
//@DocumentEntity
public class Resume {
private String education = "";

View File

@@ -12,41 +12,9 @@
<context:spring-configured/>
<context:component-scan base-package="org.springframework.persistence.test">
<context:exclude-filter expression=".*_Roo_.*" type="regex"/>
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
<!-- Store spanning config -->
<bean class="org.springframework.persistence.StoreSpanning"
factory-method="aspectOf">
<property name="mappingValidator">
<bean class="org.springframework.persistence.support.SimpleMappingValidator"/>
</property>
</bean>
<bean id="entityOperationsLocator" class="org.springframework.persistence.ChainingEntityOperationsLocator"/>
<bean class="org.springframework.persistence.ChainingForeignStoreKeyManagerLocator"/>
<bean class="org.springframework.persistence.PresentKeyForeignStoreKeyManager" autowire="constructor"/>
<bean class="org.springframework.persistence.GeneratedFieldForeignStoreKeyManager"/>
<bean class="org.springframework.persistence.document.MongoEntityOperations">
<property name="order" value="5"/>
<property name="entityInstantiator">
<bean class="org.springframework.persistence.support.ChangeSetConstructorEntityInstantiator"/>
</property>
<property name="changeSetPersister" ref="mongoChangeSetPersister"/>
</bean>
<bean class="org.springframework.persistence.EntityManagerJpaEntityOperations"/>
<bean class="org.springframework.persistence.support.ChangeSetForeignStoreKeyManager">
<property name="fieldDelimiter" value="#"/>
</bean>
<!-- Mongo config -->
<bean id="mongo" class="org.springframework.data.document.mongodb.MongoFactoryBean">
<property name="host" value="localhost"/>
@@ -61,19 +29,14 @@
<bean class="org.springframework.data.document.mongodb.MongoExceptionTranslator"/>
<!-- Mongo aspect config -->
<bean class="org.springframework.persistence.document.MongoDocumentBacking"
<bean class="org.springframework.data.persistence.document.mongo.MongoDocumentBacking"
factory-method="aspectOf">
<property name="changeSetConfiguration" ref="mongoChangeSetConfiguration"/>
</bean>
<bean id="mongoChangeSetPersister" class="org.springframework.persistence.document.MongoChangeSetPersister"/>
<bean id="mongoChangeSetSynchronizer"
class="org.springframework.persistence.support.SimpleReflectiveChangeSetSynchronizer"/>
<bean id="mongoChangeSetConfiguration" class="org.springframework.persistence.support.ChangeSetConfiguration">
<property name="changeSetPersister" ref="mongoChangeSetPersister"/>
<property name="changeSetManager" ref="mongoChangeSetSynchronizer"/>
</bean>
<!-- Needed for ChangeSet persistence -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"/>
<bean id="mongoChangeSetPersister"
class="org.springframework.data.persistence.document.mongo.MongoChangeSetPersister">
<property name="mongoTemplate" ref="mongoTemplate"/>
</bean>
<jdbc:embedded-database id="dataSource" type="HSQL">
</jdbc:embedded-database>

View File

@@ -5,8 +5,7 @@ log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.category.org.springframework=INFO
log4j.category.org.springframework.data=DEBUG
log4j.category.org.springframework.persistence=DEBUG
log4j.category.org.springframework.data=TRACE
log4j.category.org.hibernate.SQL=DEBUG
# for debugging datasource initialization