When using object/relational persistence tools like Hibernate, you may face the risk of loading your entire database if you are not using any lazy fetching strategy. There are two type of lazy associations between persistent beans: proxy (single-valued associations) and collections (such as List, Set, Bag and Map).
Granite Data Services, via its HibernateExternalizer, supports both kind of lazy associations.
In your EJB3, you may have a single-valued association like this:
@Entity public class MyEntity { @Id @GeneratedValue private Integer id; @OneToOne(fetch=FetchType.LAZY) private MyOtherEntity other; // Skipped code... } @Entity public class MyOtherEntity { @Id @GeneratedValue private Integer id; // Skipped code... }
If you load a large collection of MyEntity and don't need other references, this kind of declaration prevents unecessary performance and memory overload (please refer to EJB3/Hibernate documention in order to actually fetch those references when you need them). With GDS, you can keep those uninitialized references as is. For example:
[Bindable]
[RemoteClass(alias="path.to.MyEntity"]
public class MyEntity {
private var __laziness:String = null;
private var _id:int;
private var _other:MyOtherEntity;
// Skipped code...
public override function readExternal(input:IDataInput):void {
__laziness = input.readObject() as String;
if (__laziness === null) {
_id = input.readObject() as int;
_other = input.readObject() as MyOtherEntity;
// read remaining MyEntity fields...
}
else
_id = input.readObject() as int;
}
}
[Bindable]
[RemoteClass(alias="path.to.MyOtherEntity"]
public class MyOtherEntity {
private var __laziness:String = null;
private var _id:int;
// Skipped code...
public override function readExternal(input:IDataInput):void {
__laziness = input.readObject() as String;
if (__laziness === null) {
_id = input.readObject() as int;
// read remaining MyOtherEntity fields...
}
else
_id = input.readObject() as int;
}
}
When Flex deserialize your collection of MyEntity's with lazy loaded MyOtherEntity references, it reads a null laziness when it encounters a MyEntity instance (since MyEntitys are all initialized), so it reads all MyEntity fields (included the _other one); when it deserialize a MyOtherEntity instance referenced by a MyEntity, it reads a non null laziness (since MyOtherEntity is lazy loaded), so it only reads the MyOtherEntity id. Informations put in __laziness and _id are sufficient to recreate HibernateProxy instances when you give back MyEntity objects to the server for update.
GDS also provides a way to keep uninitialized collections as is. When the externalizer encounters an uninitialized collection, it doesn't try to serialize its content and marks it as uninitialized. This information is kept in ActionScript 3 beans and when this bean is given back to the server (eg: for an update), the externalizer re-creates a lazy initialized collection in Java. This gives you a good control over serialization depth (you do not face the risk of serializing the entire graph of your data) and prevents faulty updates (an empty collection is saved and deletes database data while it was only uninitialized).
For example (persistent set):
package test.granite.ejb3.entity; import java.util.HashSet; import java.util.Set; ... import javax.persistence.CascadeType; import javax.persistence.FetchType; import javax.persistence.OneToMany; @Entity public class Person extends AbstractEntity { ... @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="person") private Set<Contact> contacts = new HashSet<Contact>(); ... public Set<Contact> getContacts() { return contacts; } public void setContacts(Set<Contact> contacts) { this.contacts = contacts; } } // code for Contact skipped...
and:
package test.granite.ejb3.entity {
...
import org.granite.collections.UIDSet;
[Bindable]
[RemoteClass(alias="test.granite.ejb3.entity.Person")]
public class Person implements IExternalizable {
...
private var _contacts:UIDSet;
...
public function set contacts(value:UIDSet):void {
_contacts = value;
}
public function get contacts():UIDSet {
return _contacts;
}
...
public override function readExternal(input:IDataInput):void {
...
_contacts = input.readObject() as UIDSet;
...
}
public override function writeExternal(output:IDataOutput):void {
...
output.writeObject(_contacts);
...
}
}
// code for Contact skipped...
org.granite.collections.UIDSet is part of a GDS flash library (granite.swc) that contains all AS3 classes you need in order to use the lazy loaded collections feature. When a given Set is initialized, it is serialized as a simple flex.messaging.io.ArrayCollection. Since UIDSet extends ArrayCollection and its RemoteClass alias is ArrayCollection, it is silently deserialized as a UIDSet. If this Set is uninitialized, it is serialized as a org.granite.hibernate.HibernatePersistentSet (that extends org.granite.collections.UIDSet but with a RemoteClass alias marked as "org.granite.hibernate.HibernatePersistentSet").
Other persistent collections (List, Bag, Map) are handled in a similar manner. Please download graniteds-ejb3-1.0.0.zip and graniteds-src-1.0.0.zip to see example and sources.
GDS/EJB3 uses mx.core.IUID for all entity beans (see a long Hibernate discussion here about equals/hashCode/collection problems and the use of UUIDs). This is only an implementation choice and you are free to code whatever you want.
|
Browse Space |
Explore Confluence |
Your Account |
Add Content |
|
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.6.0 Build:#913 Sep 27, 2007) |
|
|
|