Skip to content

Commit

Permalink
Support for attaching additional dynamic attributes to a mongo entity
Browse files Browse the repository at this point in the history
  • Loading branch information
graemerocher committed Feb 9, 2011
1 parent edf1ea7 commit df0c193
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 12 deletions.
Expand Up @@ -16,14 +16,18 @@

package org.grails.datastore.gorm.mongo

import org.grails.datastore.gorm.GormEnhancer;
import org.grails.datastore.gorm.GormStaticApi
import org.springframework.datastore.mapping.core.Datastore;
import org.springframework.datastore.mapping.mongo.MongoDatastore;
import org.springframework.transaction.PlatformTransactionManager;
import org.grails.datastore.gorm.GormEnhancer
import org.grails.datastore.gorm.GormInstanceApi
import org.grails.datastore.gorm.GormStaticApi
import org.springframework.datastore.mapping.core.Datastore
import org.springframework.datastore.mapping.mongo.MongoDatastore
import org.springframework.datastore.mapping.mongo.MongoSession
import org.springframework.datastore.mapping.mongo.engine.MongoEntityPersister
import org.springframework.transaction.PlatformTransactionManager

import com.gmongo.internal.DBCollectionPatcher;
import com.mongodb.DBCollection;
import com.gmongo.internal.DBCollectionPatcher
import com.mongodb.DBCollection
import com.mongodb.DBObject

/**
* GORM enhancer for Mongo
Expand All @@ -46,7 +50,67 @@ class MongoGormEnhancer extends GormEnhancer {
return new MongoGormStaticApi( cls, datastore )
}

protected GormInstanceApi getInstanceApi(Class cls) {
return new MongoGormInstanceApi(cls, datastore)
}
}

class MongoGormInstanceApi extends GormInstanceApi {

public MongoGormInstanceApi(Class persistentClass, Datastore datastore) {
super(persistentClass, datastore);
}

/**
* Allows subscript access to schemaless attributes
*
* @param instance The instance
* @param name The name of the field
* @return
*/
void putAt(Object instance, String name, value) {
def dbo = getDbo(instance)
if(dbo != null) {
dbo.put name, value
}
}

/**
* Allows subscript access to schemaless attributes
*
* @param instance The instance
* @param name The name of the field
* @return
*/
def getAt(Object instance, String name) {
def dbo = getDbo(instance)
if(dbo != null && dbo.containsField(name)) {
return dbo.get(name)
}
return null
}

/**
* Return the DBObject instance for the entity
*
* @param instance The instance
*
* @return The DBObject instance
*/
DBObject getDbo(Object instance) {
MongoSession session = datastore.currentSession

if(!session.contains(instance)) {
if(!instance.save()) {
throw new IllegalStateException("Cannot obtain DBObject for transient instance, save a valid instance first")
}
}

MongoEntityPersister persister = session.getPersister(instance)
def id = persister.getObjectIdentifier(instance)

return session.getCachedEntry( persister.getPersistentEntity(), id )
}
}
class MongoGormStaticApi extends GormStaticApi {

Expand Down
@@ -0,0 +1,36 @@
package org.grails.datastore.gorm.mongo

import grails.gorm.tests.GormDatastoreSpec;
import grails.gorm.tests.Plant;

class SchemalessSpec extends GormDatastoreSpec{

def "Test attach additional data"() {
given:
def p = new Plant(name:"Pineapple")
p.dbo.color = "Yellow"
p.save(flush:true)
session.clear()

when:
p = Plant.get(p.id)

then:
p.name == 'Pineapple'
p.dbo.color == 'Yellow'
p['color'] == 'Yellow'

when:
p['hasLeaves'] = true
p.save(flush:true)
session.clear()
p = Plant.get(p.id)

then:
p.name == 'Pineapple'
p.dbo.color == 'Yellow'
p['color'] == 'Yellow'
p['hasLeaves'] == true

}
}
Expand Up @@ -128,7 +128,7 @@ public void addPendingUpdate(PendingUpdate update) {
}
public Object getCachedEntry(PersistentEntity entity, Serializable key) {
if(key != null) {
final Map<Serializable, Object> map = firstLevelEntryCache.get(entity);
final Map<Serializable, Object> map = firstLevelEntryCache.get(entity.getJavaClass());
if(map != null) {
return map.get(key);
}
Expand Down
Expand Up @@ -197,10 +197,6 @@ protected final Object retrieveEntity(PersistentEntity persistentEntity, Seriali
final Serializable key = convertToNativeKey(nativeKey);
T nativeEntry = retrieveEntry(persistentEntity, getEntityFamily(), key);
if(nativeEntry != null) {
SessionImplementor<Object> si = (SessionImplementor<Object>) session;

si.cacheEntry(persistentEntity,nativeKey, nativeEntry);

return createObjectFromNativeEntry(persistentEntity, key, nativeEntry);
}

Expand Down Expand Up @@ -233,10 +229,19 @@ public Serializable refresh(Object o) {
public Object createObjectFromNativeEntry(PersistentEntity persistentEntity, Serializable nativeKey, T nativeEntry) {
persistentEntity = discriminatePersistentEntity(persistentEntity, nativeEntry);
Object obj = persistentEntity.newInstance();

cacheNativeEntry(persistentEntity, nativeKey, nativeEntry);

refreshObjectStateFromNativeEntry(persistentEntity, obj, nativeKey, nativeEntry);
return obj;
}

protected void cacheNativeEntry(PersistentEntity persistentEntity,
Serializable nativeKey, T nativeEntry) {
SessionImplementor<Object> si = (SessionImplementor<Object>) session;
si.cacheEntry(persistentEntity,nativeKey, nativeEntry);
}

protected void refreshObjectStateFromNativeEntry(PersistentEntity persistentEntity, Object obj, Serializable nativeKey, T nativeEntry) {
EntityAccess ea = createEntityAccess(persistentEntity, obj, nativeEntry);
ea.setConversionService(getMappingContext().getConversionService());
Expand Down Expand Up @@ -370,6 +375,8 @@ protected final Serializable persistEntity(final PersistentEntity persistentEnti
tmp = createNewEntry(family);
k = generateIdentifier(persistentEntity, tmp);

cacheNativeEntry(persistentEntity, (Serializable) k, tmp);

pendingOperation = new PendingInsertAdapter<T, K>(persistentEntity, k, tmp, entityAccess) {
@Override
public void run() {
Expand Down

0 comments on commit df0c193

Please sign in to comment.