Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@Reference(lazy = true) fails on database read with circular references #608

Closed
darbie opened this issue Apr 22, 2014 · 1 comment
Closed
Labels

Comments

@darbie
Copy link

darbie commented Apr 22, 2014

When I have 2 classes, say class A and class B that both have a reference to each other using @reference, things work fine. However if I use "@reference(lazy = true) the code throws an exception on the read of one of the objects.

I am using these dependencies in my gradle project:
compile 'org.mongodb.morphia:morphia:0.1+'
compile 'cglib:cglib:2.+'
compile 'com.thoughtworks.proxytoys:proxytoys:1.+'

Here is sample code that shows the bug (the exception it produces is after the code):

import java.util.Iterator;
import java.util.UUID;

import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Morphia;
import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id;
import org.mongodb.morphia.annotations.Reference;

import com.mongodb.MongoClient;

public class Main
{

  public static void main(String[] args) throws Exception
  {
    Main main = new Main();
    main.go();
  }

  private void go() throws Exception
  {
    A a = new A(UUID.randomUUID().toString());
    B b = new B(UUID.randomUUID().toString());

    a.setB(b);
    b.setA(a);

    MongoClient mongoClient = new MongoClient();
    Morphia morphia = new Morphia();
    morphia.map(A.class, B.class);

    Datastore datastore = morphia.createDatastore(mongoClient, "dbName");
    datastore.save(a);
    datastore.save(b);

    Iterator it = datastore.createQuery(A.class).iterator();
    while (it.hasNext())
    {
      A newA = it.next();
      B newB = newA.getB();
      System.out.println(newA);
      System.out.println(newB);
    }
  }
}

@Entity(value = "collectionA", noClassnameStored = true)
class A
{
  @Id
  private String id;
  @Reference(lazy = true)
  private B b;

  private A()
  {
  }

  A(String id)
  {
    this.id = id;
  }

  void setB(B b)
  {
    this.b = b;
  }

  B getB()
  {
    return b;
  }
}

@Entity(value = "collectionB", noClassnameStored = true)
class B
{
  @Id
  private String id;
  @Reference(lazy = true)
  private A a;

  private B()
  {
  }

  B(String id)
  {
    this.id = id;
  }

  void setA(A a)
  {
    this.a = a;
  }

  A getA()
  {
    return a;
  }
}

Here is the stack trace I get:

Apr 22, 2014 1:36:43 PM org.mongodb.morphia.logging.MorphiaLoggerFactory chooseLoggerFactory
INFO: LoggerImplFactory set to org.mongodb.morphia.logging.jdk.JDKLoggerFactory
Exception in thread "main" net.sf.cglib.core.CodeGenerationException: java.lang.NoSuchMethodException-->B$$EnhancerByCGLIB$$db68ebca.()
    at net.sf.cglib.core.ReflectUtils.getConstructor(ReflectUtils.java:248)
    at net.sf.cglib.core.ReflectUtils.newInstance(ReflectUtils.java:220)
    at net.sf.cglib.proxy.Enhancer.createUsingReflection(Enhancer.java:639)
    at net.sf.cglib.proxy.Enhancer.firstInstance(Enhancer.java:538)
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:225)
    at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
    at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:304)
    at com.thoughtworks.proxy.factory.CglibProxyFactory.createWithConstructor(CglibProxyFactory.java:150)
    at com.thoughtworks.proxy.factory.CglibProxyFactory.createProxy(CglibProxyFactory.java:105)
    at com.thoughtworks.proxy.toys.hotswap.HotSwappingInvoker.proxy(HotSwappingInvoker.java:153)
    at org.mongodb.morphia.mapping.lazy.CGLibLazyProxyFactory.createProxy(CGLibLazyProxyFactory.java:33)
    at org.mongodb.morphia.mapping.ReferenceMapper.createOrReuseProxy(ReferenceMapper.java:379)
    at org.mongodb.morphia.mapping.ReferenceMapper.readSingle(ReferenceMapper.java:189)
    at org.mongodb.morphia.mapping.ReferenceMapper.fromDBObject(ReferenceMapper.java:176)
    at org.mongodb.morphia.mapping.Mapper.readMappedField(Mapper.java:608)
    at org.mongodb.morphia.mapping.Mapper.fromDb(Mapper.java:585)
    at org.mongodb.morphia.mapping.Mapper.fromDBObject(Mapper.java:296)
    at org.mongodb.morphia.query.MorphiaIterator.convertItem(MorphiaIterator.java:78)
    at org.mongodb.morphia.query.MorphiaIterator.processItem(MorphiaIterator.java:65)
    at org.mongodb.morphia.query.MorphiaIterator.next(MorphiaIterator.java:60)
    at Main.go(Main.java:41)
    at Main.main(Main.java:19)
Caused by: java.lang.NoSuchMethodException: B$$EnhancerByCGLIB$$db68ebca.()
    at java.lang.Class.getConstructor0(Class.java:2810)
    at java.lang.Class.getDeclaredConstructor(Class.java:2053)
    at net.sf.cglib.core.ReflectUtils.getConstructor(ReflectUtils.java:244)
    ... 21 more
@evanchooly evanchooly added the bug label Jun 5, 2014
@evanchooly evanchooly added this to the post-1.0 milestone Jun 5, 2014
@evanchooly
Copy link
Member

We're a little leery of the circular reference and so there's some debate about if morphia should support such a case. On the other hand, normal loads do have a cache to track already seen entities and so it would seem that it's just not being used for lazy loads. Bumping to post-1.0 for now unless there's strong sentiment otherwise.

@evanchooly evanchooly removed this from the post-1.0 milestone Jan 14, 2016
@evanchooly evanchooly added this to the 1.3.0 milestone Jun 13, 2016
@evanchooly evanchooly removed this from the 1.3.0 milestone Jul 5, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants