3

I've been trying to get a relationship working with 2 entities in AppEngine, using JPA, and am currently running into this error:

java.io.IOException: com.google.appengine.repackaged.org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain

My entities look like this:

@Entity
public class MyUser {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Key key;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL)
    private List<MyMessage> messages;
}

and this:

@Entity
public class MyMessage {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Key key;

    @ManyToOne(optional=false)
    private MyUser user;

}

The user already exists, and here is where I'm inserting a new message and get the recursion error:

EntityManager mgr = getEntityManager();
MyUser myuser = mgr.find(MyUser.class, KeyFactory.createKey("MyUser", user.getEmail()));
mymessage.setUser(myuser);
myuser.addMessage(mymessage);
mgr.persist(myuser);
mgr.persist(mymessage);

How am I supposed to setup this relationship within JPA and AppEngine guidelines? Thank you!

UPDATE

My problem was involving Jackson, not JPA. The JPA relationship is fine, but I needed to remove the relationship and manage it through the code as it was causing infinite recursion in serializing messages referring to users referring to messages and so on. I've also had to make sure that I annotated the user property in MyMessage as @Transient to avoid persistence complaining about persisting a parent owned by a child which already existed.

piusvelte
  • 1,596
  • 2
  • 15
  • 18

2 Answers2

1

I'm unaware of a reasonable way for Endpoints to serialize these classes. The JSON resulting from your current code would look smiilar to the following:

// 1
{
  "key": "foo",
  "messages": [
    {
      "key": "bar",
      "user": {
        // repeat 1
    },
  // and so on...
}

Your best bet is to define a class (or classes) to send over the wire, instead of your JPA entities, which define JSON an infinite number of levels deep.

Dan Holevoet
  • 9,183
  • 1
  • 33
  • 49
  • I'm not sure I understand why. I'm using supported annotations, based off of the examples. Why do you say that these data classes aren't supported? Also, the errors that I'm getting are related to persistence rather than cloud endpoints. Any help is appreciated, thank you! https://github.com/branflake2267/CloudEndPoints/blob/master/DemoCloudEndpoints/src/org/gonevertical/server/data/User.java – piusvelte Feb 23 '13 at 02:10
  • I'm sorry, I recalled seeing an example with a class including a list or collection property, but I cannot find one now. Despite it being discouraged for AppEngine's datastore, I guess I have to flatten out the data rdbms-style. – piusvelte Feb 23 '13 at 02:21
  • 1
    You can return a list (either as a return type or within an entity class). What's not supported is classes that refer to each other infinitely (which is what the error is about). You should define a different class that models your response in a way that doesn't result in this type of recursion, and move the data in your datastore entities to these new classes before returning them in an Endpoint. I'll update my answer to reflect this. – Dan Holevoet Feb 24 '13 at 01:09
  • I see now, and that does makes sense. Thank you! – piusvelte Feb 25 '13 at 16:05
1

Obviously your message is nothing to do with JPA persistence, it's to do with Jackson (so presumably related to how you pass those objects back?). Only you know where that is invoked from. Whether your actual persistence operation succeeds or not is not clear from your post since you don't produce the stack trace, and rest of persistence code (like where that Jackson call originates)

DataNucleus
  • 15,497
  • 3
  • 32
  • 37
  • you have a 1-N bidir relation (seems ok to me), and persist a user and a message (the pm.makePersistent(message) is unnecessary since it is persisted on the first call since connected to the user). The "log" should be configured at DEBUG level if you really want to see if things are persisted. But then I'd expect any library (including Jackson) to throw exceptions with stack traces, otherwise people will be unable to see where that software has lost the plot ... likely it has some issue with a bidir relation, so recurses infinitely. – DataNucleus Feb 23 '13 at 15:04
  • I'm using cloud endpoints with appengine, with the Jackson details handled through that SDK. So you're saying that my annotations, structure, and entity manager code all look fine? Unfortunately there wasn't more to the error in the log. – piusvelte Feb 23 '13 at 15:06
  • Thanks for the additional insight. I'm looking through the logs again, and will try removing the persist(mymessage) call. – piusvelte Feb 23 '13 at 15:09
  • I'm still getting an infinite recursion error: `java.io.IOException: com.google.appengine.repackaged.org.codehaus.jackson.map.JsonMappingException: Unable to get a resource (through reference chain: com.piusvelte.mosaic.gwt.server.MosaicUser["messages"]->java.util.ArrayList[0]-` followed by `Infinite recursion (StackOverflowError) (through reference chain: java.util.ArrayList[0]->com.piusvelte.mosaic.gwt.server.MosaicMessage["user"]-` – piusvelte Feb 23 '13 at 15:17
  • I'd guess at the answer to this question http://stackoverflow.com/questions/10191671/jackson-json-serialization-recursion-avoidance-by-level-defining – DataNucleus Feb 23 '13 at 15:35