开发者

GAE Datastore: persisting referenced objects

I a开发者_如何学Pythonm trying to persist Java objects to the GAE datastore.

I am not sure as to how to persist object having ("non-trivial") referenced object. That is, assume I have the following.

public class Father {
    String name;
    int age;
    Vector<Child> offsprings; //this is what I call "non-trivial" reference 
    //ctor, getters, setters...
}

public class Child {
    String name;
    int age;
    Father father; //this is what I call "non-trivial" reference 
    //ctor, getters, setters...
}

The name field is unique in each type domain, and is considered a Primary-Key.

In order to persist the "trivial" (String, int) fields, all I need is to add the correct annotation. So far so good. However, I don't understand how should I persist the home-brewed (Child, Father) types referenced. Should I:

  1. Convert each such reference to hold the Primary-Key (a name String, in this example) instead of the "actual" object, so Vector<Child> offsprings; becomes Vector<String> offspringsNames;?

    If that is the case, how do I handle the object at run-time? Do I just query for the Primary-Key from Class.getName, to retrieve the refrenced objects?

  2. Convert each such reference to hold the actual Key provided to me by the Datastore upon the proper put() operation? That is, Vector<Child> offsprings; becomes Vector<Key> offspringsHashKeys;?

I have read all the offical relevant GAE docs/example. Throughout, they always persist "trivial" references, natively supported by the Datastore (e.g. in the Guestbook example, only Strings, and Longs).


  • Please see google appengine docs following sections for more clear understanding (Relationships, Transactions)

  • Also read about detachable objects in JDO

  • For querying selective columns (or fields), read about fetchgroups in JDO

For your question You have several options:

  • Owned One to many relationship (the objects will be in same entity group) Here you can have a list of Child in your parent (Father class). This will place all objects in the same entity group. If you do not want to fetch the children every time you fetch Father, you can remove the children from the "default fetch group"

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Father {
   @PrimaryKey
   @Persistent
   private String name;

   @Persistent
   private int age;

   @Persistent(mappedBy = "father", defaultFetchGroup = "false")
   private List childern;
}

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Child   {
   @Persistent
   @PrimaryKey
   private String name;

   @Persistent
   private Father dad;
}

  • Unowned relationships where you store the keys instead of references:

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

   @PrimaryKey
   @Persistent
   private String name;

   @Persistent
   private int age;

   @Persistent
   private List childern;
}

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Child   {
   @Persistent
   @PrimaryKey
   private String name;

   @Persistent
   private Key dad;
}

In this case you will have to manage the referential integrity and also make sure they are in the same entity group if you have to update/add them in a single transaction

IMO, if I were modeling a real-world (Father-children) scenario, I'd go the "Owned relatinship" route, since, really, how many children a guy can have ;). Of course there is an additional question of how many fathers are you going to update at a time?

Hope this helps, cheers!


I have examples of creating parent/child relationships using GAE/JPA in my jappstart project. Take a look at how the authentication related entities are related to each other here.

One-to-One (see UserAccount.java and PersistentUser.java):

// parent
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private PersistentUser persistentUser;

// child
@OneToOne(mappedBy = "persistentUser", fetch = FetchType.LAZY)
private UserAccount userAccount;

One-to-Many (see PersistentUser.java) :

@OneToMany(mappedBy = "persistentUser", cascade = CascadeType.ALL)
private Collection<PersistentLogin> persistentLogins;

Many-to-One (see PersistentLogin.java):

@ManyToOne(fetch = FetchType.LAZY)
private PersistentUser persistentUser;

Also, note in the constructors how KeyFactory is used for entities with a parent versus without a parent.

@Id
private Key key;

// this entity has a parent
public PersistentUser(final Key key, final String username) {
    this.key = KeyFactory.createKey(key, getClass().getSimpleName(), username);
    ...
}

// this entity does not have a parent
public UserAccount(final String username) {
    this.key = KeyFactory.createKey(getClass().getSimpleName(), username);
    ....
}

Hopefully, this is helpful for you. I couldn't tell from the question whether you were using JPA or JDO.


If you have a reference to a Father in the Child and to the Children in the Father than you have the potential for inconsistency assuming that the relationship between Father and Child is two-way (ie. every Child's father should be in the list of Children for that Father). Only one of the two references is necessary.

Both solutions will work, but keeping the list of children in the father has two disadvantages:

  1. Every access to the Father object will download the list keys to the child object. If there are many keys, this could cause unnecessary overhead.
  2. I believe that GAE limits the size of a list to 5,000 items.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜