google-code-export / morphia

Automatically exported from code.google.com/p/morphia
1 stars 0 forks source link

Embedding an entity within another document returns cached entity #281

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Morphia 0.9.9 (also tested against 1.00-SNAPSHOT)

http://groups.google.com/group/morphia/browse_thread/thread/7a91fce1ca54f78b

A project I'm working on stores a history of previous states of a 
document.  We have the main document (let's call it "MyDocument"), 
which is an entity in its own right, and saved within a collection. 
The way we're saving previous states is to store a copy of this 
document within another document ("MyDocumentRevision") with a 
timestamp of when the state was changed. 
This works fine as far as persisting the data is concerned, however, 
we run into an issue when retrieving the revisions.  If we want to 
retrieve all documents for a given revision, we correctly get back 
multiple revisions.  However, the "MyDocument" references within the 
revision objects are all pointers to the same in-memory object.  This 
appears to occur because of how Morphia is determining whether to use 
a cached version of a document.  Specifically, it checks whether the 
document has a populated ID field and that it has an @Entity 
annotation: 
Mapper.java: 
Object fromDb(DBObject dbObject, Object entity, EntityCache cache) { 
  ... 
  // check the history key (a key is the namespace + id) 
  if (dbObject.containsField(ID_KEY) && 
getMappedClass(entity).getIdField() != null 
      && getMappedClass(entity).getEntityAnnotation() != null) { 
    Key key = new Key(entity.getClass(), dbObject.get(ID_KEY)); 
    Object cachedInstance = cache.getEntity(key); 
    if (cachedInstance != null) 
      return cachedInstance; 
    else 
      cache.putEntity(key, entity); // to avoid stackOverflow in 
recursive refs 
  } 
  ... 
} 

Here is my test case that I used to confirm this behavior: 
public class CreateTestDocuments{ 
  public static void main(String[] args) throws Exception{ 
    Datastore ds = getDatastore(); 
    MyDocument doc1 = new MyDocument("Version 1"); 
    ds.save(doc1); 
    MyDocumentRevision rev1 = new MyDocumentRevision(doc1); 
    ds.save(rev1); 
    doc1.setValue("Version 2"); 
    ds.save(doc1); 
    MyDocumentRevision rev2 = new MyDocumentRevision(doc1); 
    ds.save(rev2); 
  } 
  private static Datastore getDatastore() throws UnknownHostException{ 
    Mongo mongo = new Mongo(); 
    Morphia morphia = new Morphia(); 
    morphia.map(MyDocument.class).map(MyDocumentRevision.class); 
    Datastore datastore = morphia.createDatastore(mongo, 
"morphia_test_database"); 
    datastore.setDefaultWriteConcern(WriteConcern.SAFE); 
    return datastore; 
  } 
} 

public class RetrieveTestDocuments{ 
  public static void main(String[] args) throws Exception{ 
    Datastore ds = getDatastore(); 
    Iterable<MyDocumentRevision> revisions = 
ds.find(MyDocumentRevision.class).fetch(); 
    Iterator<MyDocumentRevision> i = revisions.iterator(); 
    MyDocumentRevision rev1 = i.next(); 
    MyDocumentRevision rev2 = i.next(); 
    if(rev1.getMyDocument() == rev2.getMyDocument()){ 
      // This line throws an error 
      throw new AssertionFailure("The revisions should be different 
objects"); 
    } 
  } 
  private static Datastore getDatastore() throws UnknownHostException{ 
    ... 
  } 
} 

@Entity 
public class MyDocument{ 
  @Id 
  private ObjectId id; 
  public MyDocument(){} 
  public MyDocument(String value){this.value = value;} 
  // Getters and setters are below 
  ... 
} 

@Entity 
public class MyDocument{ 
  @Id 
  private ObjectId id; 
  private String value; 
  public MyDocument(){} 
  public MyDocument(String value){this.value = value;} 
  // Getters and setters are below 
  ... 
} 

package test; 
import com.google.code.morphia.annotations.Embedded; 
import com.google.code.morphia.annotations.Entity; 
import com.google.code.morphia.annotations.Id; 
import org.bson.types.ObjectId; 
@Entity 
public class MyDocumentRevision{ 
  @Id 
  private ObjectId id; 
  @Embedded 
  private MyDocument myDocument; 
  public MyDocumentRevision(){} 
  public MyDocumentRevision(MyDocument doc){this.myDocument = doc;} 
  // Getters and setters are below 
  ... 
} 

------ 
Is there a better way I should be achieving this, or is this truly a 
bug in Morphia that should be addressed?  I can confirm that the data 
is being saved correctly in Morphia with different "value" values in 
the MyDocument document.

Original issue reported on code.google.com by matthew.j.justin on 1 Jun 2011 at 6:20

GoogleCodeExporter commented 9 years ago
I also have this problem. Because of this I have to implement a dirty 
workaround :(

Original comment by stips...@gmail.com on 17 Oct 2014 at 9:03