Rusty, where did you get that quote?  It sounds familiar...  Hell
yeah, GAE is all about wrestling with persistence since it is
Hierarchial instead of Relational and the distributed transactional
nature.  That changes an app tremendously.  Among my concerns are
being able to move an app out of GAE into a regular relational-based
container.  Sure, you can do it...but you're left with a Hierarchial
design in a relational environment...

Bryce, I'm getting this error when I try to add a Chapter...any
ideas?  Also see code below.

Attempt to handle persistence for object using datastore-identity yet
StoreManager for this datastore doesn't support that identity type
org.datanucleus.exceptions.NucleusUserException: Attempt to handle
persistence for object using datastore-identity yet StoreManager for
this datastore doesn't support that identity type
        at org.datanucleus.state.AbstractStateManager.<init>
(AbstractStateManager.java:128)
        at org.datanucleus.state.JDOStateManagerImpl.<init>
(JDOStateManagerImpl.java:215)
        at org.datanucleus.jdo.JDOAdapter.newStateManager(JDOAdapter.java:
119)
        at
org.datanucleus.state.StateManagerFactory.newStateManagerForEmbedded
(StateManagerFactory.java:131)
        at
org.datanucleus.store.appengine.DatastoreFieldManager.getEmbeddedStateManager
(DatastoreFieldManager.java:356)
        at
org.datanucleus.store.appengine.DatastoreFieldManager.storeEmbeddedField
(DatastoreFieldManager.java:811)
        at
org.datanucleus.store.appengine.DatastoreFieldManager.storeObjectField
(DatastoreFieldManager.java:769)
        at org.datanucleus.state.AbstractStateManager.providedObjectField
(AbstractStateManager.java:1037)
        at struts2.example4.Chapter.jdoProvideField(Chapter.java)
        at struts2.example4.Chapter.jdoProvideFields(Chapter.java)
        at org.datanucleus.state.JDOStateManagerImpl.provideFields
(JDOStateManagerImpl.java:2715)
        at
org.datanucleus.store.appengine.DatastorePersistenceHandler.insertPreProcess
(DatastorePersistenceHandler.java:318)
        at
org.datanucleus.store.appengine.DatastorePersistenceHandler.insertObjects
(DatastorePersistenceHandler.java:236)
        at
org.datanucleus.store.appengine.DatastorePersistenceHandler.insertObject
(DatastorePersistenceHandler.java:225)
        at org.datanucleus.state.JDOStateManagerImpl.internalMakePersistent
(JDOStateManagerImpl.java:3185)
        at org.datanucleus.state.JDOStateManagerImpl.makePersistent
(JDOStateManagerImpl.java:3161)
        at org.datanucleus.ObjectManagerImpl.persistObjectInternal
(ObjectManagerImpl.java:1298)
        at org.datanucleus.sco.SCOUtils.validateObjectForWriting
(SCOUtils.java:1476)
        at
org.datanucleus.store.mapped.scostore.ElementContainerStore.validateElementForWriting
(ElementContainerStore.java:380)
        at
org.datanucleus.store.mapped.scostore.FKListStore.validateElementForWriting
(FKListStore.java:609)
        at org.datanucleus.store.mapped.scostore.FKListStore.internalAdd
(FKListStore.java:344)
        at org.datanucleus.store.mapped.scostore.AbstractListStore.add
(AbstractListStore.java:105)
        at org.datanucleus.sco.backed.List.add(List.java:649)
        at org.apache.jsp.example4_jsp.addChapter(example4_jsp.java:21)
        at org.apache.jsp.example4_jsp._jspService(example4_jsp.java:88)

/** Example from GAE/J forum Bryce show use of embedded class BookFk.
*/
@PersistenceCapable(identityType=IdentityType.APPLICATION,
detachable="true")
public class Book {
        @PrimaryKey
        @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
        private Key bookId;

        @Persistent
        private String bookTitle;
        @Persistent
        private String isbn;
        @Persistent
        private List<Chapter> chapters;

    public Book(String bookTitle, String isbn) {
        this.bookTitle = bookTitle;
        this.isbn = isbn;
    }

        public Key getBookId() { return bookId; }

        public String getBookTitle() { return bookTitle; }
        public void setBookTitle(String val) { bookTitle = val; }

        public String getIsbn() { return isbn; }
        public void setIsbn(String val) { isbn = val; }

        public List<Chapter> getChapters() { return chapters; }
        public void setChapters(List<Chapter> val) { chapters = val; }
}

@PersistenceCapable(identityType=IdentityType.APPLICATION,
detachable="true")
public class Chapter {
        @PrimaryKey
        @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
        private Key chapterId;

        @Persistent
        private String chapterTitle;
        @Persistent
        private Integer numPages;

        @Persistent
        @Embedded
        private BookFk bookFk;

    public Chapter(String chapterTitle, Integer numPages, Book book) {
        this.chapterTitle = chapterTitle;
        this.numPages = numPages;
        bookFk = new BookFk(book);
    }

    public Key getChapterId() { return chapterId; }

        public String getChapterTitle() { return chapterTitle; }
        public void setChapterTitle(String val) { chapterTitle = val; }

        public Integer getNumPages() { return numPages; }
        public void setNumPages(Integer val) { numPages = val; }
}

@PersistenceCapable(detachable="true")
public class BookFk {
        @Persistent
        private Key bookId;
        @Persistent
        private String bookTitle;
        @Persistent
        private String isbn;

    public BookFk(Book book) {
        bookId = book.getBookId();
        bookTitle = book.getBookTitle();
        isbn = book.getIsbn();
    }

    public Key getBookId() { return bookId; }
    public void setBookId(Key val) { bookId = val; }

    public String getBookTitle() { return bookTitle; }
    public void setBookTitle(String val) { bookTitle = val; }

    public String getIsbn() { return isbn; }
    public void setIsbn(String val) { isbn = val; }
}

---- snippets from JSP driver
...
        private Book addBook(PersistenceManager pm, String title, String
isbn) throws Exception {
                Book book = new Book(title, isbn);
                return pm.makePersistent(book);
        }
        private Chapter addChapter(PersistenceManager pm, String title,
Integer numPages, Book book) throws Exception {
                Chapter chapter = new Chapter(title, numPages, book);
                book.getChapters().add(chapter);
                //return pm.makePersistent(chapter);
                return null;
        }
...
Book newBook = addBook(pm, "Gone with the wind", "123");
...
addChapter(pm, "chapter 1 - rhett", new Integer(40), newBook);

NOTE: The book gets added ok, but then the chapter Add fails.  I've
also tried them in separate transactions with same error.  I'll test
and search this error further, Thanks.

On Nov 4, 1:02 am, bryce cottam <bcot...@gmail.com> wrote:
> Hi James,
> sorry for the delay,
>
> My design is currently similar to what you proposed.  However, in your
> example, I'd probably make 2 classes: PersonFK and InstitutionFK.:
> public class PersonFK {
>      private Key id;
>      private String firstName;
>      private String lastName;
>      private String city;
>
> }
>
> public class InstitutionFK {
>      private Key id;
>      private String name;
>      private String city;
>
> }
>
> Then in Person you would have:
>
> public class Person {
>     private List<InstitutionFK> institutions;
>     ....
>
> }
>
> and in Institutions you would have:
> public class Institution {
>     private List<PersonFK> persons;
>     ....
>
> }
>
> (I'm leaving out the annotations for brevity).
>
> each of these member fields (institutions and persons) are "owned" by
> their containing instance of Person and Institutions.  Thus, the
> relationship is defined by who owns it (i.e we don't need the
> Institution's id, name, city etc. on the persons collection because we
> already know what Institution the PersonFK's are linked to)
> So, yes, anytime you want to do something like update the Person.city
> field, you'd want to get all PersonFK instances with your person-Id,
> then update each instance with the "new" city value.  Of course, this
> only matters if you plan on querying Institutions based off of the
> "city" field of a Person.  That generally would imply multiple
> transactions, however there is the Wilkerson distributed transaction
> model, which makes "global" transactions on GAE possible.  So, if your
> writes really do need to be transactional, then you can do an "all or
> nothing" operation on multiple entity groups with this approach.
>
> here is a video from Google I/O about distributed transactions on the 
> GAE:http://www.youtube.com/watch?v=IOtBlzsx0m8
>
>
>
> On Tue, Nov 3, 2009 at 1:50 PM, James H <james.hollier...@gmail.com> wrote:
>
> > Consider this test case.  A Person belongs to 1 or more Institutions
> > so there's 2 ways you would want to query this.  Query #1: Given a
> > particular Person then which Institutions does he belong to?  Query
> > #2:  Given a particular Institution then which Persons are members?
>
> > Assume entity Person and its associated child relations will form its
> > own entity group and, likewise, entity Institution will form its own
> > entity group related to its children.  In order to accommodate large
> > amounts of Person and Institution data, I submit that you would need
> > "owned" relations of each.
>
> > Therefore, you would need entity PersonInstitutions as an owned child
> > relation with Person for Query #1.  Similarly, you would need entity
> > InstitutionPersons as an owned child relation with Institution for
> > Query #2.
>
> > Also, each of these 2 child entities would contain both a PersonFK and
> > a InstitutionFK with appropriate redundant fields ancillary to the
> > query requirement of the app.  Again, the embedded FK class avoids
> > extra queries that would kill search list performance, etc.  For
> > example with Query #2, give me all persons at Baylor that have first
> > name James would only need the entity InstitutionPersons.
>
> > So, we accept the fact the design requires redundant data in the
> > embedded FK class but at least from a source code perspective we only
> > have FK class to manage.
>
> > Is it correct that we would need both "owned" relations or not?  Say
> > you had Persons and Institutions from around the world.  I can't
> > imagine satisfying the above 2 queries without both "owned"
> > relations.  Of course, this means updates to both relations are across
> > 2 entity groups and separate transactions.  Another fact we have to
> > accept in the design right?
>
> > On Nov 3, 2:22 pm, James H <james.hollier...@gmail.com> wrote:
> >> Ok Bryce...I'm back.  Going to test with your ideas now.  In my case,
> >> I tend to avoid generic column names like "id" in favor of "bookId"
> >> and "chapterId" so I should not have any naming conflicts (at least
> >> rarely).  Also, my FK embedded classes should not have any collections
> >> though I have a feeling they may come up as I get further into it.
>
> >> I'll just test with Book and owned relation Chapter as you have it
> >> here.  How do we tell JDO whether a relation is "owned" vs "unowned"
> >> by the way?  Because I would like to have a child object of each for
> >> the testing.  Thanks!
>
> >> On Oct 9, 12:06 pm, bryce cottam <bcot...@gmail.com> wrote:
>
> >> > FYI, this video was hugely helpful for me, and this information helps
> >> > me decide how to structure my data model to run best on the
> >> > app-engine, it's a Google I/O session on how the app-engine big table
> >> > implementation works:http://www.youtube.com/watch?v=tx5gdoNpcZM
>
> >> > On Fri, Oct 9, 2009 at 10:46 AM, James H <james.hollier...@gmail.com> 
> >> > wrote:
>
> >> > > Ylmz, thats how I see it too.  Any significant data model will be
> >> > > riddled with FKs and since GAE datastore does not support Joins you
> >> > > have NO choice but to denormalize your data to meet the needs of your
> >> > > app queries.  The Cottam Pattern above allows best management of
> >> > > source to accomplish this denormalization.
>
> >> > > I assume the simple example you are referring to either only stores
> >> > > the Keys in the physical table (which would be insufficient for app
> >> > > queries) or stores the whole record (which would be massive overkill).
>
> >> > > Does that make any sense?
>
> >> > > On Oct 8, 4:46 am, bryce cottam <bcot...@gmail.com> wrote:
> >> > >> yes, I think the point that we are making is that we are trying to
> >> > >> managed "un-owned" relationships  :)
> >> > >> You are describing owned relationships, which I use as well, but in
> >> > >> many cases, I really want un-owned relationships.  For a variety of
> >> > >> reasons really, one of which is that the children I have in my
> >> > >> collection get get rather big, and there is a cap on the number of
> >> > >> writes that can happen on an entity group.  So, in come cases it's
> >> > >> better to model the relationships as foreign keys.
>
> >> > >> The problem comes in when you try to manage your data.  It's a pretty
> >> > >> common suggestion from the app-engine team to denormalize, we're just
> >> > >> trying to come up with a way to denormalize and minimize code
> >> > >> duplication etc.  Denormalizing will let you query better/faster etc.
> >> > >> but can create headaches when trying to update a single field which is
> >> > >> mirrored on several other entities.
>
> >> > >> For instance, you could have an Employee that works for several
> >> > >> departments in a company.  It's the same Employee, so it should be a
> >> > >> single record.  So, in this case you'd have a Company, Division and
> >> > >> Employee.  A Company "owns" it's Divisions and it also "owns" it's
> >> > >> Employees, yet a Division "owns" it's Employees as well.  So, where
> >> > >> does the collection of Employees live?  On the Company, or the
> >> > >> Division?  If it's the Division, then you can't share employees with
> >> > >> other divisions (unless you duplicate the Employee record).  If the
> >> > >> Company owns the Employee, then you have to make some way for the
> >> > >> Division to know who's in it.  You could do this by putting a
> >> > >> collection of Division Key objects on an Employee record, or a
> >> > >> collection of Employee Keys on a Division record.  Yet, this doesn't
> >> > >> allow for simple querying, like "select all employees that work in a
> >> > >> division located in New York and has less than 20 people in it".  Or,
> >> > >> "select all divisions who have an employee named Fred".
>
> >> > >> you can do queries like this if you denormalize the data though  :)
>
> >> > >> On Thu, Oct 8, 2009 at 3:35 AM, ylmz <yilmazhuse...@gmail.com> wrote:
>
> >> > >> > may I suggest different kind of desing
> >> > >> >http://code.google.com/appengine/docs/java/datastore/relationships.html,
> >> > >> > in this document it suggests to use collection types to design one 
> >> > >> > to
> >> > >> > many relation ships.
> >> > >> > and it works pretty well actually. when you use a collection type. 
> >> > >> > app
> >> > >> > engine does not really use the original colection type you used.
> >> > >> > instead
> >> > >> > it uses its own replacement. so if you want to add a new employee to
> >> > >> > the company you just add a new one to the list. it automatically 
> >> > >> > added
> >> > >> > to datastore.
> >> > >> > and if I understand right, when you get a company you dont get all 
> >> > >> > the
> >> > >> > employees inside. you only get a collection type which is actulally
> >> > >> > empty but look like it has employees inside.
> >> > >> > so when you get any element from collection type
> >> > >> > it pulls that record from datastore.
> >> > >> > there is also some collection types that excepts unique values.
> >> > >> > Is there a spesific reason you don't use collections that I didn't
> >> > >> > understand?
>
> >> > >> > On Oct 7, 4:16 am, James H <james.hollier...@gmail.com> wrote:
> >> > >> >> Wow, this is majorly useful...can't wait to try it out!!!  For the
> >> > >> >> life of me I can't figure out why this problem domain has NOT 
> >> > >> >> already
> >> > >> >> been exhausted in this Group since denormalization is a requirement
> >> > >> >> with this technology for any significant business application!
>
> >> > >> >> I believe your example should be placed in a section on how to 
> >> > >> >> handle
> >> > >> >> Advanced Relations w/Denormalization in this section of the docs:
>
> >> > >> >>http://code.google.com/appengine/docs/java/datastore/relationships.html
>
> >> > >> >> Great work, Bryce!  I'll see if I can break it :)
>
> >> > >> >> As for our other achilles' heel, that being Distributive
> >> > >> >> Transactions...do you think the Open Source team will deliver any 
> >> > >> >> time
> >> > >> >> soon?  I worry about contention in that solution since everything 
> >> > >> >> is
> >> > >> >> concentrated into 1 table - the DT instruction table...  Meanwhile,
> >> > >> >> I'm thinking about queuing
>
> ...
>
> read more »- Hide quoted text -
>
> - Show quoted text -
--~--~---------~--~----~------------~-------~--~----~
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-java@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
-~----------~----~----~----~------~----~------~--~---

Reply via email to