4. Lazy Initialization

Lazy Initialization (Generalities)

When using object/relational persistence tools like Hibernate, TopLink, EclipseLink, OpenJPA or even DataNucleus, you may face the risk of loading your entire database if you are not using any lazy fetching strategy. There are two types 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 (or any other specific JPA externalizer), supports both kinds of lazy associations.

The next chapters explain how basic lazy initialization works in GDS, taking Hibernate as an example (but these explanations applies to other JPA engines as well). You may also have a look to advance lazy initialization features provided by the Tide data framework here.

Single-Valued Associations (HibernateProxy weaved association)

In your JPA entity bean, 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 do not need other references, this kind of declaration prevents unnecessary performance and memory overload (please refer to Hibernate documention in order to actually fetch these 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 __initialized:Boolean = true;
    private var __detachedState:String = null;

    private var _id:Number;
    private var _other:MyOtherEntity;

    meta function isInitialized(name:String = null):Boolean {
        if (!name)
            return __initialized;

        var property:* = this[name];
        return (
            (!(property is Welcome) || (property as Welcome).meta::isInitialized()) &&
            (!(property is IPersistentCollection) ||
              (property as IPersistentCollection).isInitialized())
        );
    }

    // Skipped code...

    public override function readExternal(input:IDataInput):void {
        __initialized = input.readObject() as Boolean;
        __detachedState = input.readObject() as String;
        if (meta::isInitialized()) {
            _id = function(o:*):Number {
                return (o is Number ? o as Number : Number.NaN) } (input.readObject());
            _other = input.readObject() as MyOtherEntity;
            // read remaining MyEntity fields...
        }
        else
            _id = function(o:*):Number {
                return (o is Number ? o as Number : Number.NaN) } (input.readObject());
    }
}

[Bindable]
[RemoteClass(alias="path.to.MyOtherEntity"]
public class MyOtherEntity {

    private var __initialized:Boolean = true;
    private var __detachedState:String = null;

    private var _id:Number;

    // Skipped code...

    public override function readExternal(input:IDataInput):void {
        __initialized = input.readObject() as Boolean;
        __detachedState = input.readObject() as String;
        if (meta::isInitialized()) {
            _id = input.readObject() as int;
            // read remaining MyOtherEntity fields...
        }
        else
            _id = input.readObject() as int;
    }
}

When Flex deserializes your collection of MyEntity's with lazy loaded MyOtherEntity references, it reads a intialized flag set to true when it encounters a MyEntity instance since MyEntity's are all initialized; so it reads all MyEntity fields including the _other one. When it deserializes a MyOtherEntity instance referenced by a MyEntity, it reads a initialized flag set to false since MyOtherEntity is lazy loaded, so it only reads the MyOtherEntity id. Informations put in __initialized, __detachedState and _id are sufficient to recreate HibernateProxy instances when you give back MyEntity objects to the server for update.

Collections (List, Set, Bag, Map)

GDS also provides a way to keep uninitialized collections as is. When the externalizer encounters an uninitialized collection, it does not 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 (e.g., for an update), the externalizer recreates a lazy initialized collection in Java. This gives you a good control over serialization depth, as you do not face the risk of serializing the entire graph of your data, and prevents faulty updates (i.e., an empty collection is saved and deletes database data while it was only uninitialized).

For example, in this 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 mx.collections.ListCollectionView;

    [Bindable]
    [RemoteClass(alias="test.granite.ejb3.entity.Person")]
    public class Person implements IExternalizable {

        ...
        private var _contacts:ListCollectionView;
        ...
        public function set contacts(value:ListCollectionView):void {
            _contacts = value;
        }
        public function get contacts():ListCollectionView{
            return _contacts;
        }
        ...
        public override function readExternal(input:IDataInput):void {
            ...
            _contacts = input.readObject() as ListCollectionView;
            ...
        }
        public override function writeExternal(output:IDataOutput):void {
            ...
            output.writeObject(_contacts);
            ...
        }
}

// code for Contact skipped...

The actual, persistence aware, mx.collections.ListCollectionView implementation is part of a GDS Flex library (granite-essentials.swc) that contains all AS3 classes you need in order to use the lazy loaded collections feature.

If GDS encounters an uninitialized Set, it is serialized as a org.granite.persistence.PersistentSet that contains some extra data indicating its intitialization state.

Other persistent collections, such as List, Bag, and Map, are handled in a similar manner. Please download graniteds-2.0.0.GA.zip and look at the examples/granite_ejb3 sample for a more advanced data model.

GDS/JPA 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

- Pages
- Blog
- Labels
- Attachments
- Bookmarks
- Mail
- Advanced

Explore Confluence

- Popular Labels
- Notation Guide

Your Account

Log In

Other Features

Add Content


Copyright © 2011 Granite Data Services S.A.S. All Rights Reserved.