Standard AMF3 serialization/deserialization does not provide any way to transfer private or protected data fields (neither with Flex 2 Data Services nor with Granite DS): only non static, non transient public fields (either those with public getter and setter or with a public declaration) are taken into account.
| This Granite DS behavior slightly differ from Flex 2 one's: with Flex 2, you may also serialize read only fields (ie: private or protected field with only a getter). |
To (de)serialize private or protected fields you must use an externalization mecanism (see Flex 2 documentation). Granite DS provides a way to avoid the Java coding overload of writing your entity beans as Externalizable. Further more, it provides a way to make Externalizable standard Java classes (such as java.util.Locale).
In order to externalize our Person.java entity bean (and to keep its private field id and version), we must provide a rule in a granite-config.xml file:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE granite-config PUBLIC "-//Granite Data Services//DTD granite-config internal//EN" "http://www.graniteds.org/public/dtd/1.0.0/granite-config.dtd"> <granite-config> <externalizers> <externalizer type="org.granite.hibernate.HibernateExternalizer"> <include type="test.granite.ejb3.entity.Person"/> </externalizer> </externalizers> </granite-config>
This instructs Granite DS to externalize all test.granite.ejb3.entity.Person beans by using the org.granite.hibernate.HibernateExternalizer. If you use an abstract entity bean as a parent to all your entity beans you could use this declaration (note that type in the example above is replaced by instanceof):
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE granite-config PUBLIC "-//Granite Data Services//DTD granite-config internal//EN" "http://www.graniteds.org/public/dtd/1.0.0/granite-config.dtd"> <granite-config> <externalizers> <externalizer type="org.granite.hibernate.HibernateExternalizer"> <include instanceof="test.granite.ejb3.entity.AbstractEntity"/> </externalizer> </externalizers> </granite-config>
This will avoid the overload of writing externalization instructions for all your beans (all instances of AbstractEntity will be automatically externalized).
You may even use a annotatedwith attribute as follow:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE granite-config PUBLIC "-//Granite Data Services//DTD granite-config internal//EN" "http://www.graniteds.org/public/dtd/1.0.0/granite-config.dtd"> <granite-config> <externalizers> <externalizer type="org.granite.hibernate.HibernateExternalizer"> <include annotatedwith="javax.persistence.Entity"/> <include annotatedwith="javax.persistence.MappedSuperclass"/> <include annotatedwith="javax.persistence.Embeddable"/> </externalizer> </externalizers> </granite-config>
Of course, you may mix those different attributes as you want.
| Default location for your granite-config.xml file is: <YOUR_WAR>/WEB-INF/granite/granite-config.xml. You may put this file in another location provided that you specify this path in your web.xml. See the sample web.xml file in granite-ejb3-1.0.0.zip. |
| You may write your own externalizers and plug them with the same kind of declaration in granite-config.xml. |
Now we have defined an automated externalization for our Person.java bean (see previous section), we must write a corresponding ActionScript 3 class:
package test.granite.ejb3.entity {
import flash.utils.IExternalizable;
import flash.utils.IDataInput;
import flash.utils.IDataOutput;
[Bindable]
[RemoteClass(alias="test.granite.ejb3.entity.Person")]
public class Person implements IExternalizable {
private var __laziness:String = null;
private var _id:int;
private var _version:int;
private var _firstName:String;
private var _lastName:String;
public function get id():int {
return _id;
}
public function set firstName(value:String):void {
_firstName = value;
}
public function get firstName():String {
return _firstName;
}
public function set lastName(value:String):void {
_lastName = value;
}
public function get lastName():String {
return _lastName;
}
public override function readExternal(input:IDataInput):void {
__laziness = input.readObject() as String;
if (__laziness === null) {
_firstName = input.readObject() as String;
_id = input.readObject() as int;
_lastName = input.readObject() as String;
_version = input.readObject() as int;
}
else
_id = input.readObject() as int;
}
public override function writeExternal(output:IDataOutput):void {
output.writeObject(__laziness);
if (__laziness === null) {
output.writeObject(_firstName);
output.writeObject(_id);
output.writeObject(_lastName);
output.writeObject(_version);
}
else
output.writeObject(_id);
}
}
}
Laziness field indicates if this bean is fully initialized (__laziness === null) or not (ie: an HibernateProxy). If this bean is initialized, we may read all other fields. Otherwise, only the bean id should be read (a proxy contains this information and its value is necessary in order to recreate the proxy instance at deserialization time). See more informations about lazy loading support in the next section.
| Order of fields reading and writing (readExternal/writeExternal) is important! It must be done in lexical order ("firstName" < "id" < "lastName" < "version")! Automated externalization must observe a predictable way when reading or writing fields by Java reflexion (laziness must be always first). |
For a more complex example with EJB3 inheritance, please download graniteds-ejb3-1.0.0.zip.
| Granite Data Services 0.3 comes with an AS3 bean generator for EJB3 that avoids the tedious task of writing your own AS3 beans by hand. See ActionScript 3 Beans Generation. |
|
Browse Space |
Explore Confluence |
Your Account |
Add Content |
|
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.6.0 Build:#913 Sep 27, 2007) |
|
|
|