On Mon, Jan 25, 2010 at 11:52 PM, John Patterson <jdpatter...@gmail.com> wrote: > > This is why you configure what type of relationship is used using: @Embed, > @Entity(PARENT), @Entity(CHILD) or @Entity(INDEPENDENT) > So you have the flexibility to choose configuration _without_ rewriting your > code. Very important difference.
The problem is that it *isn't* as simple as just changing an annotation. Not in appengine, at any rate. It works in the simple case (good for demos and sample apps), but you start to notice edge cases: * In some representations you can add a Photo to an Album in a transaction, in some representations you can't. * In some representations, multiple queries are required to fetch a fetch an Album containing a Photo. * Each representation has a completely different query syntax. Twig exposes the datastore Query object, which means the developer gets the full brunt of this exposure. I don't want to say that it isn't possible to build a system that abstracts entities into a sophisticated object graph - clearly, we have JDO & JPA. What I'm saying is that the people who created JDO & JPA are not idiots (although I do think the creators of JDO's annotations are aesthetically challenged). The reason JDO has all that complexity and endless configuration and query languages and fetch groups and proxies and detaching and whatnot is because that's what you need to abstract an arbitrary entity graph. > Currently the first type of representation is not an option. I do want to > add this as it makes very large collections that change often much more > efficient. When it is added you could reconfigure your data schema by > changing a single annotation. Such a change in Objectify would require the > developer to rewrite their entire data layer You can never just reconfigure your data schema with a single annotation, both for the reasons above and because you probably have real-world data to migrate. And real-world constraints demand a particular schema! Let's actually answer the original poster's question - how do you model a photo album? I hope he's still listening :-) The first question is how you should model it in the datastore? I'll use Objectify's syntax here because it corresponds directly to the datastore representation. Actually, let's start by describing how you SHOULDN'T model a photo album. ----- BAD IDEA #1: class Album { @Id Long id; String name; List<OKey<Photo>> photos; } class Photo { @Id Long id; String caption; String blobStoreKey; // key to GAE's blobstore } Fetching photos in an album (again, Objectify syntax but equivalent to the datastore operation) is: List<Photo> fetched = ofy.get(album.photos); There are two reasons why this is a bad idea: 1) You now have a hard limit of 5,000 photos per album, established by GAE. 2) Every time you load an Album, you must load the entire set of Photo keys. Want to generate a list of Album names? You have to load all that key data, orders of mangitude more data than what you want. ----- BAD IDEA #2: class Album { @Id Long id; String name; } class Photo { @Id Long id; @Parent OKey<Album> album; String caption; String blobStoreKey; } This stores the Photo with the Album embedded in the Photo's Key as an ancestor, making Photo part of the Album's entity group. At first glance, this seems kinda cool and you can now do transactions across Albums and Photos. Fetching photos in an album: OQuery<Photo> query = createQuery(Photo.class).ancestor(albumKey); List<Photo> fetched = ofy.prepare(query).asList(); The problem is what happens when you want to move a Photo from one Album to another. You can't just change the parent. You must delete the Photo entity and create a whole new Photo entity with the new parent Album. And if the Photo has Comments or other referring entities? All those comments need to be repointed at the new Photo. A mess. ----- GOOD IDEA: class Album { @Id Long id; String name; } class Photo { @Id Long id; OKey<Album> album; String caption; String blobStoreKey; } Fetching photos in an album: OQuery<Photo> query = createQuery(Photo.class).filter("album", albumKey); List<Photo> fetched = ofy.prepare(query).asList(); You can now move Photos between Albums easily and you can load/query Albums efficiently. ----- GOOD IDEA #2: I considered writing something about index entities as described here: http://code.google.com/events/io/2009/sessions/BuildingScalableComplexApps.html This is what you would probably want to use if a Photo can live in more than one Album. But this message is long enough already. ----- So now you're thinking, that's just the representation in the datastore, wouldn't you rather have a entities that hide all that and provide an interface like this: class Album { @Id Long id; String name; List<Photo> photos; } class Photo { @Id Long id; String caption; String blobStoreKey; } This is one of those things that looks good in a simple demo but in the real world breaks down. Twig doesn't currently support mapping this to GOOD IDEA. If Twig did support this, it would also need to support lazy loading of the collection with a proxy - otherwise you have a worse problem than BAD IDEA #1! And now, if this list is a proxy, serialization becomes an issue - you need some sort of detaching mechanism. ...and then you have JDO. I hope this message doesn't sound like "Twig is bad" - it's not, and it does (and probably always will) do things that Objectify does not. I just want to discourage the notion that you can easily work on Appengine without using the Key class. Judging by the official sample code, this isn't even easy using JDO! Jeff -- 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.