Part5 - Serialized Fields

你好,欢迎来到第5part的“JDO/JPA Snippets That Work”,今天我们将讨论的是...

Serialized Fields

假设你有个model objects拥有一个非native datastore的(例如String或者Date)同时也不是一个有主,一对一关系的model object的字段。通常你可以在一条记录里用一个Embedded class包含住些record来保存这个字段的子字段。但如果Embedded class不够用的时候呢?

这时就可以用Serialized Fields了。

用jdo和jpa可以保存任何实现了java.io.Serializable接口的single prperty。

为了我们的例子程序,我们建立一个Person,一个ContactProfiles,一个ContactProfile, 和一个PhoneNumber的模型。一个Person只用一个ContactProfiles实例,而,一个ContactProfiles有多个ContactProfile实例,同时,一个ContactProfile有多个PhoneNumber实例。

现在我们程序要求是这样:当我们从datastore里retrieve一个Person时,需要同时load入这个Person的ContactProfiles.我们首先先用jdo或jpa来定义ContactProfiles,ContactProfile和PhoneNumber类先。
public static class ContactProfiles implements Serializable { 
    private final List<ContactProfile> profiles; 
    public ContactProfiles(List<ContactProfile> profiles) { 
      this.profiles = profiles; 
    } 
    public List<ContactProfile> get() { 
      return profiles; 
    } 
} 

public class ContactProfile implements Serializable { 
    private String profileName; 
    private List<PhoneNumber> phoneNumbers; 
    // getters and setters 
} 

public class PhoneNumber implements Serializable { 
    private String type; 
    private String number; 
    // getters and setters 
} 

JPA方式定义的Person类如下:
@Entity 
public class Person { 
    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    private Key id; 
    @Lob 
    private ContactProfiles contactProfiles; 
    // getters and setters 
} 

JDO方式定义的Person类如下:
@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = 
"true") 
public class Person { 
    @PrimaryKey 
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) 
    private Key id; 
    @Persistent(serialized = "true") 
    private ContactProfiles contactProfiles; 
    // getters and setters 
} 


在这个例子里面,App Engine实现将Person.contactProfiles字段转换为一个实现了标准Java
serialization接口的com.google.appengine.api.datastore.Blobusing类。
这个Blob将以single property的形式存储在Person Entity里面。当你fetch一个Person的时候,相应的Blob将会经过一个独立的datastore rpc请求被反序列化,你将得到一个ContactProfiles实例和全部他关联的子对象。

当用这种做法时,有一些缺点你是应该意识到的。

第一,虽然你的应用知道ContactProfiles的结构,但datastroe并不知道,这导致你不能过滤或排序ContactProfile和PhoneNumber.第二,所有已知java serialization存在的问题都适用在这里。当你改进你的Serializable classes的时候,请必须考虑向后兼容。第三,你可能在观察ContactProfiles这个类的时候已经发现,为什么我们要用一个这么麻烦的ContactProfiles类而不直接用一个List<ContactProfile>呢?其原因就是update一个serialized field并不是好像update其他字段类型那么简单。DataNucleus并没有办法“看”得见你修改了serialized field,这就意味着他不会更新数据到datastore。然而,DataNucleus可以看到顶层的serialized field的改变。给Person类一个ContactProfiles一个引用可以便利我们去修改ConactProfiles而DataNucles又保证能意识到。
public void addContactProfile(Person p, ContactProfile cp) { 
    // update our serialized field 
    p.getContactProfiles().get().add(cp); 
    // give the person a new ContactProfiles reference so the update is 
detected 
    p.setContactProfiles(new ContactProfiles(p.getContactProfiles().get())); 
} 

最后一行我们设回一个新的ContactProfiles实例给Person是关键,同时也容易忘记的,所以请小心!如果你忘记这一步,你的更新将被忽略。

尽管我列出了以上的不足,但Serialized fields是一个保存structured data到一个记录到containing Entity的好方法。只要你不需要过滤和排序serialized fields,同时你记得这个特别的更新数据的要求,在某些时候serialized fields也无疑是一个方便的方法。


以下是原文
Hello hello and welcome to Episode 5 of JDO/JPA Snippets That Work.  Today
we'll be discussing....

Serialized Fields

Suppose one of your model objects has a field that isn't a native datastore
type like String or Date and isn't another model object, like the child of
an owned OneToOne relationship.  Oftentimes you can use an Embedded
class<http://code.google.com/appengine/docs/java/datastore/dataclasses.html...>to
store that field's sub-fields in the same record as the containing
record, but what if an Embedded class isn't sufficient?  This is where
Serialized Fields come in.  With JDO and JPA it's possible to store any
class that implements java.io.Serializable in a single property.
For our example lets model Person, ContactProfiles, ContactProfile, and
PhoneNumber.  A Person has exactly one ContactProfiles instance, a
ContactProfiles has some number of ContactProfile instances, and a
ContactProfile has some number of PhoneNumber instances.  Our application is
such that when we retrieve a Person from the datastore it always makes sense
to load their ContactProfiles as well.  We'll define ContactProfiles,
ContactProfile, and PhoneNumber first since they're the same whether we're
using JPA or JDO:

public static class ContactProfiles implements Serializable { 
    private final List<ContactProfile> profiles; 
    public ContactProfiles(List<ContactProfile> profiles) { 
      this.profiles = profiles; 
    } 
    public List<ContactProfile> get() { 
      return profiles; 
    } 
} 

public class ContactProfile implements Serializable { 
    private String profileName; 
    private List<PhoneNumber> phoneNumbers; 
    // getters and setters 
} 

public class PhoneNumber implements Serializable { 
    private String type; 
    private String number; 
    // getters and setters 
} 


The JPA Person definition:

@Entity 
public class Person { 
    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    private Key id; 
    @Lob 
    private ContactProfiles contactProfiles; 
    // getters and setters 
} 


The JDO Person definition:

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = 
"true") 
public class Person { 
    @PrimaryKey 
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) 
    private Key id; 
    @Persistent(serialized = "true") 
    private ContactProfiles contactProfiles; 
    // getters and setters 
} 


In this example the App Engine JDO/JPA implementation converts the
Person.contactProfiles field into a
com.google.appengine.api.datastore.Blobusing standard Java
serialization.  This Blob is then stored as a single
property on the Person Entity.  When you fetch a Person the corresponding
Entity is retrieved and the Blob is deserialized in just a single datastore
rpc, leaving you with a ContactProfiles instance and all its associated
sub-objects.

There are a few drawbacks to this approach that you should be aware of.
First, even though your application understands the structure of the bytes
that make up the ContactProfiles, the datastore does not.  As a result you
can't filter or sort on any of the properties of ContactProfile or
PhoneNumber.  Second, all the known gotchas associated with Java
serialization apply here.  If you evolve your Serializable classes make sure
you do it in a backwards compatible way!  Third, you might be looking at the
definition of the ContactProfiles class and thinking "Why are we bothering
with a ContactProfiles class when Person could just store
List<ContactProfile>?"  The reason is that updating a serialized field is
not as simple as updating other types of fields, and this extra layer of
indirection makes things a bit easier.  DataNucleus has no way of "seeing"
the changes you make to the internal values of a serialized field, and as a
result it doesn't know when it needs to flush changes to these fields to the
datastore.  However, DataNucleus *can* see when the top-level serialized
field reference changes.  Giving Person a reference to a ContactProfiles
object makes it easy for us to change ContactProfiles in a way that
DataNucleus is guaranteed to recognize:
public void addContactProfile(Person p, ContactProfile cp) { 
    // update our serialized field 
    p.getContactProfiles().get().add(cp); 
    // give the person a new ContactProfiles reference so the update is 
detected 
    p.setContactProfiles(new ContactProfiles(p.getContactProfiles().get())); 
} 


The last line where we set a new ContactProfiles instace back on the Person
is the key and it's easy to miss, so be careful!  If you forget this step
your updates will be ignored and you will be sad.
Despite the limitations I've listed, Serialized fields are a good way to
store structured data inside the record of a containing Entity.  As long as
you can get by without filtering or sorting on this data and you remember
that it has special update requirements, serialized fields will almost
certainly come in handy at some point.

Until next time,
Max

转载自: http://groups.google.com/group/google-appengine-java/browse_thread/thread/747ceed8396c0ed8#

说明:上面的中文翻译是我用自己能理解的方式翻译出来的,并不是逐字翻译,只作为自己参考用,并不十分精确。

其实个人觉得这个做法必须用到的地方可能不多哦。

你可能感兴趣的:(thread,数据结构,jpa,Google,UP)