Google App Engine + JAVA + JDO + Inheritance + One-To-Many Relationships

Objective

This article will show you how to persist real world data in Google App Engine. You will learn how to handle inheritance and one-to-many owned relationships in your model objects and store them correctly in the big table implementation of JDO.

The Problem Domain

In my application I need to segregate data by the customer this data belongs to. We will use inheritance in model objects, so all of them will have customer id defined in the base class. We will make the JDO save base class properties in the sub class “table”. We will also learn how to instruct Google App Engine JDO to handle customers with multiple addresses and phone numbers for us. If we define the relationship between model objects correctly JDO will manage storing/retrieving/deleting of these lined objects without our involvement.

Inheritance in Model Classes

First let’s define a superclass for all our model classes:

package com.yourcorp.here;

import java.io.Serializable;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.Inheritance;
import javax.jdo.annotations.InheritanceStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
@Inheritance(strategy=InheritanceStrategy.SUBCLASS_TABLE)
public class ModelBase implements Serializable{

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;
    @Persistent
    protected Long customerId;

    private static final long serialVersionUID = 3039288063578312662L;

    // getters & setters go here
}

What we done here is defined Model Base class that has two properties – ID and customerID and marked them as persistent. We also annotated that our class is persistence capable and defined Inheritance strategy. In this case we chose to use Subclass Table. What this means is that ID and customerID properties will be saved in the table of the class that inherits this one. You can read more about inheritance strategies here.

Now lets define our User class:

package com.yourcorp.here;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
public class User extends ModelBase implements Serializable {

    @Persistent
    private String email;
    @Persistent
    private String name;
    @Persistent
    private String password;

    private static final long serialVersionUID = 3039288063578312662L;

    // getters & setters go here
}

When you persist an instance of this User class in the big table it will have id, customerId, email, name and password properties in the single user “table”.

One-to-many relationships in Google App Engine JDO

For this example lets define a customer class that has a list on phone and address objects:

package com.yourcorp.here;

import java.util.ArrayList;
import java.util.List;

import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.Element;

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
public class Customer extends ModelBase {

    @Persistent
    private String name;
    @Persistent
    private String contactName;
    @Persistent
    private String comments;

    @Persistent(mappedBy = "customer")
    @Element(dependent = "true")
    private List<Address> adresses = new ArrayList<Address>();

    @Persistent(mappedBy = "customer")
    @Element(dependent = "true")
    private List phones = new ArrayList();

    private static final long serialVersionUID = 3039288063578312662L;

    // getters &amp; setters go here
}
</address>

As you can tell we have defined two lists and annotated them to be persistent. (mappedBy = “customer”) defines this relationship as “owned” – meaning the customer property of the Address and Phone object will hold the reference tho this customer object.
And here is an example of the address object:

package com.yourcorp.here;

import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
public class Address extends EntityBase {

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key id;
    @Persistent
    private String type;
    @Persistent
    private String name;
    @Persistent
    private String line1;
    @Persistent
    private String line2;
    @Persistent
    private String city;
    @Persistent
    private String state;
    @Persistent
    private String zip;
    @Persistent
    private Customer customer;

    private static final long serialVersionUID = 3039288063578312662L;

    // getters &amp; setters go here
}

You may also notice @Element(dependent = “true”) which means that when the customer object is deleted JDO will delete these child objects for you automatically.

Conclusion

JDO simplifies your life when you need to store and retrieve data if you know how to define all these relationships correctly. Compare the effort required to store and retrieve the same object from your favorite relational database, without using the monster called hibernate……

20 thoughts on “Google App Engine + JAVA + JDO + Inheritance + One-To-Many Relationships

  1. Alexander,

    For the purpose of this example you do not need to implement Serializable. I copied code form a bigger project where I serialize objects over the wire to the client app and back.

    Thanks,
    Tomas

  2. Tomas,

    could you help me a bit if you have time?
    yesterday I was trying to model a simple forum comments. Just a
    comments which you can add more comments to.
    So, the approximation of the entity is like this:
    ——————————————————————-
    @PersistenceCapable (detachable = “true”, identityType =
    IdentityType.APPLICATION)
    public class CommentEntity {

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private String category;

    @Persistent
    private String commentDate;

    @Persistent
    private String userName;

    @Persistent
    private String commentBody;

    @Persistent
    private List children = new
    ArrayList();

    getters/setters/etc.
    }

    DataNucleus enhancement goes ok, no errors in the console.
    Call of the pm.makePersistent() goes without any exceptions for such
    entity.
    BUT IT IS NOT PERSISTED.

    I found out that problem is in “children” property. And the problem is
    it’s a list of objects of the same class as the parent entity. If I
    comment that property declaration the entity is persisted. Also if I
    change the type of the child entities to some new class (not extending
    the CommentEntity) it also gets persisted.

    So my suspicion is that JDO (or GAE JDO impl) does not allow child
    entities to be of the same class. Is it correct? Maybe I’m missing
    something essential? Please advice.

  3. Yes, I think you are running into a limitation of big table JDO implementation. This creates kind of cyclic reference.
    You will need to manage the relationship by hand on this one. Store a list of child keys to you can recreate the thread of comments. And have the parent for all comments be a thread, so you can pull them out in one query and contruct the thread by hand.
    Some things are more work then you expect 😉

  4. Hi

    i have been trying to persist a object with arraylist of objects (1-many relationship) however i always ran into some problem or other. and finally decided to use the example here … but again ran into a problem .. the error is

    Caused by: java.lang.IllegalArgumentException: addresses: test.shared.Address is not a supported property type.

    I am using the following code to persist the object person with addresses

    ArrayList aa = new ArrayList();
    aa.add(new Address(“123 sasdf”, “”, “Some City”, “AZ”, “93923-2321”));
    aa.add(new Address(“23432 asdf”, “Appt 34”, “Another City”, “AZ”, “43434-4432”));
    ArrayList parray = new ArrayList();
    Person p = new Person();
    p.setName(“VVVVVVV”);
    p.setAge(23);
    p.setGender(‘m’);
    p.setAddresses(aa);
    PersistenceManager pm = PMF.get().getPersistenceManager();
    pm.makePersistent(p);
    pm.close();

    by the looks of it … i am sure there is something very simple which i am missing but not able to put a finger to it.

    Please help…

  5. Tomas,

    could you help me a bit if you have time?
    yesterday I was trying to model a simple forum comments. Just a
    comments which you can add more comments to.
    So, the approximation of the entity is like this:
    ——————————————————————-
    @PersistenceCapable (detachable = “true”, identityType =
    IdentityType.APPLICATION)
    public class CommentEntity {

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private String category;

    @Persistent
    private String commentDate;

    @Persistent
    private String userName;

    @Persistent
    private String commentBody;

    @Persistent
    private List children = new
    ArrayList();

    getters/setters/etc.
    }

    DataNucleus enhancement goes ok, no errors in the console.
    Call of the pm.makePersistent() goes without any exceptions for such
    entity.
    BUT IT IS NOT PERSISTED.

    I found out that problem is in “children” property. And the problem is
    it’s a list of objects of the same class as the parent entity. If I
    comment that property declaration the entity is persisted. Also if I
    change the type of the child entities to some new class (not extending
    the CommentEntity) it also gets persisted.

    So my suspicion is that JDO (or GAE JDO impl) does not allow child
    entities to be of the same class. Is it correct? Maybe I’m missing
    something essential? Please advice.

  6. Bruce,

    Your observation is correct – you can not have children of the same class.
    My hunch is that it is due to the implementation of the Key class.
    Each child Key holds reference to parent key/id. In theory it should be possible to have child of the same type, but I think current implementation does not allow it. Maybe to safeguard for infinite loop where you make same object child and parent……

    Tomas

  7. Hi Tomas, thank you for the excellent example!!

    I’m struggling with a strange problem with JDO.
    I’ve got two PersistenCapable classes, one having a Collection of
    objects of the second, something like this:

    class First {
    @Persistent
    @PrimaryKey
    Long id;

    @Persistent(mappedby=”owner”)
    ArrayList list = new ArrayList();

    ArrayList getList() {
    if (list == null)
    list=new ArrayList();
    return list;
    }


    }

    class Second {
    @Persistent
    @PrimaryKey
    Key id;

    @Persistent
    First owner;

    First getOwner() {
    if (owner==null)
    owner = new First();
    return owner;


    }

    Seems to be quite the same as you example, right?

    In another class I need to print the owner of all my First objects, so
    I do:
    First obj = …;
    ArrayList list = obj.getList();
    for (Second s : list) {
    System.out.println(s.getOwner());
    }

    In this loop, I find some Second object having null owner, and I
    cannot understand why.
    Now I have several questions about my data modelling:
    1) Do I need to mark any field with (defaultFetchGroup = “true”)
    annotation?
    2) Does the check on null object (e.g. if (owner==null) owner = new
    First();) in the getter methods results in any strange behavior?
    3) Does the assignment on definition of objects (e.g.
    ArrayList list = new ArrayList();) results in any
    strange behavior?
    4) Do I need to add any other annotation to owner field of Second
    class?

    Thank you very much for your help!!
    Best regards
    cghersi

  8. Hello,
    i am wandering something in a relationship 1/N normally you could do like cghersi’s example. But GWT’s client part needs source code of the classes it uses, so Key source code is needed. At execution time GWT will complains. How would you create a 1/N relationship without a Key class ?
    (i saw a workaround creating a source code of the Key.java but it’s just a hack)
    Thanks

  9. I recently stumbled on your blog and happen to be reading along. I imagined I would go away my 1st remark. I don’t know what to say except that I’ve enjoyed reading.Great web site,I’ll retain visiting this blog site quite frequently.

  10. Hi Thomas, very good example 🙂
    Can i ask you a simple questiion please?
    How can i write a query that returns all the children objects of the parent?
    In this example let’s say all addresses of the customer.

  11. That’s an excellent post, thank you. One thing I am still struggling to get my head around is how to run a query that will return parent objects filtered by some property of the child object.
    Can you give an example of how you would get a list of all customers with and address where city = “New York”?

  12. My brother informed me with regards to your site. They were right, I’m really astounded with all the publishing and slick layout. It appears in my experience you’re basically scratching the surface on the subject of whatever you might carry out, yet you’re off towards a excellent start! I decided to include this site to my personal bookmark webpage along with I recently joined your rss.

  13. Hi

    Based on GAE JDO page (http://code.google.com/appengine/docs/java/datastore/jdo/relationships.html), I create Employee and ContactInfo objects with relation one to many.

    public class Employee implements Serializable{
    @Persistent(mappedBy = “employee”, defaultFetchGroup = “true”)
    @Element(dependent = “true”)
    private List contactInfoSets;

    public class ContactInfo implements Serializable{
    @Persistent
    private String streetAddress;

    I try to exec below code always give me error (java.lang.AssertionError: expected: but was:) because but the return value is 0.

    String statement = “SELECT FROM ” + Employee.class.getName()
    + ” WHERE contactInfoSets.contains(i) && i.streetAddress==’myaddress'”
    + ” VARIABLES ” + ContactInfo.class.getName() + ” i “;
    javax.jdo.Query query = pm.newQuery(statement);
    List companyProfiles = (List) query.execute();

    Thanks…

    Here the code

    **[Employee.java]**

    import java.io.Serializable;
    import java.util.List;

    import javax.jdo.annotations.IdGeneratorStrategy;
    import javax.jdo.annotations.IdentityType;
    import javax.jdo.annotations.PersistenceCapable;
    import javax.jdo.annotations.Persistent;
    import javax.jdo.annotations.PrimaryKey;
    import javax.jdo.annotations.Element;

    import com.google.appengine.api.datastore.Key;

    @SuppressWarnings(“serial”)
    @PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = “true”)
    public class Employee implements Serializable{

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent(mappedBy = “employee”, defaultFetchGroup = “true”)
    @Element(dependent = “true”)
    private List contactInfoSets;

    public List getContactInfoSets() {
    return contactInfoSets;
    }
    public void setContactInfoSets(List contactInfoSets) {
    this.contactInfoSets = contactInfoSets;
    }
    public Key getKey() {
    return key;
    }
    public void setKey(Key key) {
    this.key = key;
    }
    }

    **[ContactInfo.java]**

    import java.io.Serializable;

    import javax.jdo.annotations.IdGeneratorStrategy;
    import javax.jdo.annotations.IdentityType;
    import javax.jdo.annotations.PersistenceCapable;
    import javax.jdo.annotations.Persistent;
    import javax.jdo.annotations.PrimaryKey;

    import com.google.appengine.api.datastore.Key;

    @SuppressWarnings(“serial”)
    @PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = “true”)
    public class ContactInfo implements Serializable{

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private String streetAddress;

    @Persistent
    private Employee employee;

    public Key getKey() {
    return key;
    }

    public void setKey(Key key) {
    this.key = key;
    }

    public String getStreetAddress() {
    return streetAddress;
    }

    public void setStreetAddress(String streetAddress) {
    this.streetAddress = streetAddress;
    }

    public Employee getEmployee() {
    return employee;
    }

    public void setEmployee(Employee employee) {
    this.employee = employee;
    }
    }

    **[TestJdoCollection.java]**

    import static com.google.appengine.api.datastore.FetchOptions.Builder.withLimit;
    import static org.junit.Assert.assertEquals;

    import java.util.ArrayList;
    import java.util.List;

    import javax.jdo.PersistenceManager;
    import javax.jdo.PersistenceManagerFactory;

    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;

    import com.cea.common.service.CeaServiceFactory;
    import com.cea.common.service.PersistenceService;
    import com.google.appengine.api.datastore.DatastoreService;
    import com.google.appengine.api.datastore.DatastoreServiceFactory;
    import com.google.appengine.api.datastore.Query;
    import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
    import com.google.appengine.tools.development.testing.LocalServiceTestHelper;

    public class TestJdoCollection {
    private final LocalServiceTestHelper helper = new LocalServiceTestHelper(
    new LocalDatastoreServiceTestConfig());

    private DatastoreService ds = DatastoreServiceFactory.getDatastoreService();

    @Before
    public void setUp() {
    helper.setUp();
    }

    @After
    public void tearDown() {
    helper.tearDown();
    }

    @Test
    public void test(){
    CeaServiceFactory ceaServiceFactory = CeaServiceFactory.getInstance();
    PersistenceService servicePersistance = (PersistenceService) ceaServiceFactory.getCeaService(CeaServiceFactory.SERVICE_PERSISTANCE);
    PersistenceManagerFactory pmf = servicePersistance.getPersistenceManagerFactory();
    PersistenceManager pm = pmf.getPersistenceManager();

    ContactInfo contactInfo = new ContactInfo();
    contactInfo.setStreetAddress(“myaddress”);

    Employee employee = new Employee();
    List contactInfoSets = new ArrayList();
    contactInfoSets.add(contactInfo);
    employee.setContactInfoSets(contactInfoSets);

    pm.makePersistent(employee);

    assertEquals(1, ds.prepare(new Query(“Employee”)).countEntities(withLimit(10)));
    assertEquals(1, ds.prepare(new Query(“ContactInfo”)).countEntities(withLimit(10)));

    String statement = “SELECT FROM ” + Employee.class.getName() + ” WHERE contactInfoSets.contains(i) && i.streetAddress==’myaddress'”
    + ” VARIABLES ” + ContactInfo.class.getName() + ” i “;
    javax.jdo.Query query = pm.newQuery(statement);
    List companyProfiles = (List) query.execute();
    assertEquals(1, companyProfiles.size());

    }
    }

Leave a Reply

Your email address will not be published. Required fields are marked *