Search code examples
javagoogle-app-enginepersistencejdo

One-to-many relationship children throw "One or more instances could not be made persistent"


I have two classes, each of which are PersistentCapable- an Event and a Score class. The Event class contains an ArrayList of Scores.

I have a method that builds up a List of Events, then tries to save the Events and their corresponding Scores to the datastore using

PersistenceManager pm = PMF.get().getPersistenceManager();
        try {
            pm.makePersistentAll(Event.getEvents());
        } finally {
            pm.close();
        }

The Events all save to the datastore fine (I can see them all at /_ah/admin). The Scores, however, do not save (at least most of them don't).

All of the scores from the first Event save, but non of the other Event's Scores do. All of the events have scores, and the lists are populated prior to trying to save to the datastore.

My call to makePersistentAll() is also throwing javax.jdo.JDOUserException: One or more instances could not be made persistent

Finally, I can see in the datastore admin that though the Events are all being saved, their score value is a list of a bunch (~15) null values. All other values are correct. The Keys are all unique as well (though I am manually generating those).

Any ideas on what is going wrong?

Event:

@PersistenceCapable(detachable="true")
public class Event {
    @NotPersistent
    protected static ArrayList<Event> events = new ArrayList<Event>();

    @PrimaryKey
    @Persistent
    private Key key;

    @Persistent
    private String name;
    @Persistent
    private String date;
    @Persistent
    private String year;
    @Persistent
    private String scoresUrl;
    @Persistent
    private ArrayList<Score> scores;

Score:

@PersistenceCapable(detachable="true")
public class Score {
    @PrimaryKey
    @Persistent
    private Key key;
    @Persistent
    private Key eventKey;
    @Persistent
    private String unitName;
    @Persistent
    private int place;
    @Persistent
    private float score;

After setting .level = FINEST in logging.properties, I see three entries that I think might be causing the problem just after all of the Events are saved. It appears as though the connection is being closed before the makePersistentAll() can get down to the Score objects.

Aug 17, 2012 2:15:42 PM org.datanucleus.store.connection.ConnectionManagerImpl closeAllConnections
FINE: Connection found in the pool : com.google.appengine.datanucleus.DatastoreConnectionFactoryImpl$DatastoreManagedConnection@77a477b7 for key=org.datanucleus.ObjectManagerImpl@45e4d960 in factory=ConnectionFactory:nontx[com.google.appengine.datanucleus.DatastoreConnectionFactoryImpl@4eafccbe] but owner object closing so closing connection
Aug 17, 2012 2:15:42 PM org.datanucleus.store.connection.ConnectionManagerImpl$1 managedConnectionPostClose
FINE: Connection removed from the pool : com.google.appengine.datanucleus.DatastoreConnectionFactoryImpl$DatastoreManagedConnection@77a477b7 for key=org.datanucleus.ObjectManagerImpl@45e4d960 in factory=ConnectionFactory:nontx[com.google.appengine.datanucleus.DatastoreConnectionFactoryImpl@4eafccbe]
Aug 17, 2012 2:15:42 PM com.google.apphosting.utils.jetty.JettyLogger warn
WARNING: /scores
javax.jdo.JDOUserException: One or more instances could not be made persistent

Solution

  • As per Shivan Dragon's suggestion, I moved from using a PersistenceManager to doing everything within a transaction.

    It doesn't look nearly as neat (because I need to explicitly set each property), but it now works as expected and no longer throws an Exception.

    I'm decidedly okay with the extra clutter, as this method also gives me better control over when to and when not to save my data for individual Entities to the datastore.