Max Ross said... ...Please post your question on the Google Group for GAE Java (the link is in the App Engine Links section on the right) along with your model objects and the code you're using to query for the child objects.... ================================================================
I have been attempting to do an owned one-to-many and have followed the example here. I have also tried for more than 10 hours experimenting with sight variations (looking at other examples) and using the debugger. I save the parent with a long list of children with no erros. No matter what I do it when I query and get the parent, the child list comes back empty. My datastore now has 10s of parent records. One difference is that my fields are public. Is that the problem? I don't think it could be. Another difference is that when I use: public List chapters = new ArrayList(); When I try not using generics on the ArrayList I get the following runtime error: Jan 12, 2010 8:56:59 PM com.google.appengine.tools.development.ApiProxyLocalImpl log SEVERE: [1263329819732000] javax.servlet.ServletContext log: Exception while dispatching incoming RPC call com.google.gwt.user.server.rpc.UnexpectedException: Service method 'public abstract java.lang.String lt.client.GreetingService.greetServer (java.lang.String)' threw an unexpected exception: org.datanucleus.jdo.exceptions.ClassNotPersistenceCapableException: The class "The class "lt.server.Book" is not persistable. This means that it either hasnt been enhanced, or that the enhanced version of the file is not in the CLASSPATH (or is hidden by an unenhanced version), or the Meta-Data/annotations for the class are not found." is not persistable. This means that it either hasnt been enhanced, or that the enhanced version of the file is not in the CLASSPATH (or is hidden by an unenhanced version), or the Meta-Data for the class is not found. I don't get the ClassNotPersistenceCapableException runtime error if I declare the list as: public List⟨Chapter⟩ chapters = new ArrayList〈Chapter〉 (); However, I am unable to query and get back any of the items that were in the list. I have also tried @Persistent (defaultFetchGroup = "true") private Book theBook; in the child, and that gets this warning: Jan 12, 2010 8:06:34 PM org.datanucleus.store.appengine.MetaDataValidator warn WARNING: Meta-data warning for lt.server.Lyric.theBook: The datastore does not support joins and therefore cannot honor requests to place child objects in the default fetch group. The field will be fetched lazily on first access. You can modify this warning by setting the datanucleus.appengine.ignorableMetaDataBehavior property in your config. A value of NONE will silence the warning. A value of ERROR will turn the warning into an exception. To clarify the above, I think I have tried everything except private fields and a non-generic ArrayList. I have never been able to get the child records back, although I get the parent. (I have edited the above to change from Song to Book and Lyric to Chapter to agree with your example) Here is my query code: public lt.client.Song getSong(String input) { lt.client.Song result = null; Song theSong = null; PersistenceManager pm = pmfInstance.getPersistenceManager(); try { String query = "select from " + Song.class.getName() + " where name == lastNameParam " + " parameters String lastNameParam "; List<Song> songs = (List<Song>) pm.newQuery(query).execute(input); for (Song aSong : songs) theSong = aSong; List<lt.client.Lyric> ll = new ArrayList<lt.client.Lyric>(); if (theSong != null) { if (theSong.lyrics != null) for (Lyric l : (List<Lyric>)theSong.lyrics) { ll.add(new lt.client.Lyric(l.measure, l.words)); } if (ll.size() == 0) { List<String> emptyLyric = new ArrayList<String>(); emptyLyric.add("emptyLyric"); ll.add(new lt.client.Lyric("EmptyMeasure", emptyLyric)); } result = new lt.client.Song(theSong.name, ll); } } finally { pm.close(); } if (result != null) return result; return new lt.client.Song("Song not found", new ArrayList<lt.client.Lyric>()); } } Here are my classes: package lt.server; import java.io.*; import java.util.*; import javax.jdo.annotations.*; import javax.jdo.*; import javax.persistence.Entity; import com.google.appengine.api.datastore.Key; @PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true") public class Song implements Serializable { @SuppressWarnings("unused") @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private com.google.appengine.api.datastore.Key key; @Persistent public String name; @Persistent(mappedBy = "theSong") @Element(dependent = "true") //public List<Lyric> lyrics = new ArrayList<Lyric>(); public List lyrics = new ArrayList(); public void initSong(PersistenceManager pm, Reader input) { StreamTokenizer st = new StreamTokenizer(input); name = ""; st.resetSyntax(); st.wordChars('\u0021', '\u00FE'); st.whitespaceChars('\u0000', '\u0020'); st.eolIsSignificant(true); st.slashSlashComments(false); List<String> words = new ArrayList<String>(); String measure = ""; try { while (st.nextToken() != StreamTokenizer.TT_EOL && st.ttype != StreamTokenizer.TT_EOF) { name += (st.sval + " "); } if (st.ttype != StreamTokenizer.TT_EOF) { while (st.nextToken() == StreamTokenizer.TT_EOL) ; if (st.ttype != StreamTokenizer.TT_EOF) { if (st.sval.substring(0, 1).equals("|")) { if (st.sval.length() > 1) measure = st.sval.substring(1); else { while (st.nextToken() == StreamTokenizer.TT_EOL) ; if (st.ttype != StreamTokenizer.TT_EOF) { measure = st.sval; } } } else { st.pushBack(); measure = ""; } if (st.ttype != StreamTokenizer.TT_EOF) { boolean pendingLyric = false; while (st.nextToken() != StreamTokenizer.TT_EOF) { if (st.ttype == StreamTokenizer.TT_EOL) { pendingLyric = true; while (st.nextToken() == StreamTokenizer.TT_EOL) ; } if (st.ttype != StreamTokenizer.TT_EOF) { if (st.sval.substring(0, 1).equals("|")) { pendingLyric = false; lyrics.add(new Lyric(pm, this, measure, words)); words = new ArrayList<String>(); if (st.sval.length() > 1) measure = st.sval.substring(1); else { while (st.nextToken() == StreamTokenizer.TT_EOL) ; if (st.ttype != StreamTokenizer.TT_EOF) { measure = st.sval; } } } else { if (pendingLyric) { pendingLyric = false; lyrics.add(new Lyric(pm, this, measure, words)); words = new ArrayList<String>(); measure = ""; } words.add(st.sval); } } } } } } } catch (IOException ioe) { } if (words.size() > 0) lyrics.add(new Lyric(pm, this, measure, words)); } } package lt.server; import java.io.Serializable; import java.util.*; import javax.jdo.annotations.*; import javax.jdo.*; @PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true") public class Lyric implements Serializable { @SuppressWarnings("unused") @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private com.google.appengine.api.datastore.Key key; @SuppressWarnings("unused") //@Persistent (defaultFetchGroup = "true") @Persistent private Song theSong; @Persistent public List<String> words; @Persistent public String measure; public Lyric(PersistenceManager pm, Song theSong, String measure, List<String> words) { //pm.makePersistent(this); //this.theSong = theSong; this.measure = measure; this.words = words; //pm.makePersistent(this); } } The things that are commented out are left over from variations I have tried in the past. As I said I have done well over 10 hours of experimenting. I'm a professor of computer science and constantly find the errors in my students' code, but I can't seem to crack this one.
-- You received this message because you are subscribed to the Google Groups "Google App Engine for Java" group. To post to this group, send email to google-appengine-j...@googlegroups.com. To unsubscribe from this group, send email to google-appengine-java+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/google-appengine-java?hl=en.