Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

< Previous PageNext Page > Hide TOC

Implementing an Enterprise Object

As discussed in “Which Enterprise Object Class?,” one of the first decisions you need to make about an enterprise object is what class it maps to. In many cases, an enterprise object should map to EOGenericRecord. Enterprise object classes that include custom business logic should map to custom subclasses of EOGenericRecord. In some cases, particularly for advanced developers, an enterprise object can map to a custom subclass of EOCustomObject.

From the perspective of Enterprise Objects, these three choices are identical: they all interact with the technology in the same way because they all implement the EOEnterpriseObject interface. That interface specifies a complete set of methods that support common operations for all types of enterprise objects. It includes methods for initializing enterprise object instances, announcing changes to other enterprise objects, setting and retrieving property values in enterprise objects, and performing validation of enterprise object values.

One of the three types of enterprise object class mappings should be sufficient for all circumstances. You should never need to implement the EOEnterpriseObject interface from scratch. Most of the methods in EOEnterpriseObject are meant to be used internally, and besides, the default implementations of the interface provide the default behavior you want.

In this section:

Generating Source Files
Fields
Change Notification
Faulting


Generating Source Files

The first step in creating a custom enterprise object is with EOModeler’s Generate Java Files and Generate Client Java Files commands. These commands take the entities, attributes, and relationships you’ve defined in a model to generate a corresponding enterprise object class. Before generating a class file for a particular entity, you must assign a class name to that entity. You assign custom class names to entities in EOModeler’s table mode. While in table mode, select the root of the tree view to display the model’s entities in table mode, as “Figure 3-3” illustrates.

In this table, you specify the class name to use for an entity. The class name is used when you generate source files for that entity.


Figure 2-3  Custom classes for entities

Custom classes for entities

EOModeler warns you if you try to generate a source file for an entity that hasn’t been assigned a custom class name. When you specify a custom class name for an entity, in most cases EOModeler generates a class file whose class member inherits from EOGenericRecord. However, if you’re using inheritance, a child entity’s class definition is generated to inherit from its parent.

The class that EOModeler generates for the entity described in “Reference Entity” appears in “Listing 3-1.” It provides public accessor methods for the entity’s class properties, but not for the entity’s primary key, listingID, which is not selected as a class property.

Listing 2-1  Generated class for simplified Listing entity (EOGenericRecord)

import com.webobjects.foundation.*;
import com.webobjects.eocontrol.*;
import java.math.BigDecimal;
import java.util.*;
 
public class Listing extends EOGenericRecord {
 
    public Listing() {
        super();
    }
 
    public Number bedrooms() {
        return (Number)storedValueForKey("bedrooms");
    }
 
    public void setBedrooms(Number value) {
        takeStoredValueForKey(value, "bedrooms");
    }
 
    public Number bathrooms() {
        return (Number)storedValueForKey("bathrooms");
    }
 
    public void setBathrooms(Number value) {
        takeStoredValueForKey(value, "bathrooms");
    }
 
    public EOEnterpriseObject address() {
        return (EOEnterpriseObject)storedValueForKey("address");
    }
 
    public void setAddress(EOEnterpriseObject value) {
        takeStoredValueForKey(value, "address");
    }
 
    public NSArray features() {
        return (NSArray)storedValueForKey("features");
    }
 
    public void setFeatures(NSArray value) {
        takeStoredValueForKey(value, "features");
    }
 
    public void addToFeatures(EOEnterpriseObject object) {
        includeObjectIntoPropertyWithKey(object, "features");
    }
 
    public void removeFromFeatures(EOEnterpriseObject object) {
        excludeObjectFromPropertyWithKey(object, "features");
    }
}

If you prefer to use EOCustomObject subclasses, you can modify the class in “Listing 3-1” as shown in “Listing 3-2.”

Listing 2-2  Generated class for simplified Listing entity (EOCustomObject)

import com.webobjects.foundation.*;
import com.webobjects.eocontrol.*;
import java.math.BigDecimal;
import java.util.*;
 
public class Listing extends EOCustomObject {
 
    private Double _bathrooms = null;
    private Double _bedrooms = null;
    private EOEnterpriseObject _address = null;
    private NSArray _features = null;
 
    public Listing() {
        super();
    }
    public static boolean usesDeferredFaultCreation() {
        return true;
    }
    public Double bathrooms() {
        willRead();
        return _bathrooms;
    }
 
    public void setBathrooms(Double value) {
        willChange();
        _bathrooms = value;
    }
 
    public Double bedrooms() {
        willRead();
        return _bedrooms;
    }
 
    public void setBedrooms(Double value) {
        willChange();
        _bedrooms = value;
    }
    public EOEnterpriseObject address() {
        willRead();
        willReadRelationship(_address);
        return _address;
    }
 
    public void setAddress(EOEnterpriseObject value) {
        willChange();
        _address = value;
    }
    public NSArray features() {
        willRead();
        willReadRelationship(_features);
        return _features;
    }
 
    public void setFeatures(NSArray value) {
        willChange();
        _features = value;
    }
 
    public void addToFeatures(EOEnterpriseObject object) {
        includeObjectIntoPropertyWithKey(object, "features");
    }
 
    public void removeFromFeatures(EOEnterpriseObject object) {
        excludeObjectFromPropertyWithKey(object, "features");
    }
}

These two code listings demonstrate the primary differences between EOGenericRecord subclasses and EOCustomObject subclasses. “Listing 3-1” contains no fields for the object’s attributes; they are stored in a dictionary that the class maintains internally. Contrast this with “Listing 3-2,” which includes fields for the object’s attributes: _bathrooms, _bedrooms, _address, and _features.

“Listing 3-2” includes accessor methods that explicitly act on the object’s fields, whereas the accessor methods in “Listing 3-1” use key-value coding to access the object’s data in its internal dictionary.

“Listing 3-2,” being a class that inherits from EOCustomObject, explicitly calls willChange immediately before a property is mutated and calls willRead immediately before a property is accessed. These invocations are required in EOCustomObject subclasses to support faulting and change notification; they are invoked automatically in EOGenericRecord subclasses, so they aren’t present in the class in “Listing 3-1.”

Finally, “Listing 3-2” implements a feature of Enterprise Objects called deferred faulting, which is discussed in “Deferred Faulting.” It implements this feature by overriding usesDeferredFaultCreation and by invoking willReadRelationship in accessor methods that return values from the object’s relationships. “Listing 3-1” uses deferred faulting automatically because it is an EOGenericRecord subclass.

Fields

As discussed in “Generating Source Files,” EOModeler generates class files whose class members inherit from EOGenericRecord. By default, EOGenericRecords do not include fields for their properties. Rather, they store their properties in a dictionary that can be accessed using key-value coding accessors. This both reduces the amount of code you need to write and makes your custom enterprise object classes easier to maintain.

That said, you can add fields for an enterprise object’s properties to an EOGenericRecord subclass if you want to, or you can use an EOCustomObject in which you are required to use fields, as “Listing 3-2” illustrates.

Change Notification

In Enterprise Objects, objects that need to know about changes to an enterprise object register as observers for particular change notifications. When an enterprise object is about to change, it is responsible for posting a notification so that registered observers are notified that it’s about to change. To do this, enterprise object instances invoke willChange prior to altering their state. Whenever you add your own methods that change an object’s state, you need to include this invocation.

When invoked in an EOGenericRecord subclass, the stored value accessor (takeStoredValueForKey), automatically invokeswillChange for you, as a convenience. However, in enterprise object class that includes fields for its properties, this isn’t the case, so you must invoke willChange yourself, as the class in “Listing 3-2” does.

The fact that change notification is handled for you is another reason why some people prefer to use EOGenericRecord subclasses.

Faulting

When an enterprise object is about to retrieve data for one or more of its properties, it is required to notify other objects that it’s about to take action. It does this by invoking willRead. An EOGenericRecord class using takeStoredValueForKey to access data automatically invokes willRead.

This method is a part of Enterprise Object’s faulting mechanism. Faulting is the mechanism by which Enterprise Objects delays the full initialization of an enterprise object instance until that object’s data is actually required. You can think of faulting as creating a shell of an enterprise object that includes just some (or perhaps none) of its data. See “Figure 6-3” for an illustration.

Faulting reduces memory consumption and provides a performance improvement to applications by delaying fetches to the database until data is actually needed. Database fetches are expensive, especially during the resolution of relationships. Often, an enterprise object needs a reference to a particular relationship but doesn’t necessarily need the data in that relationship. By providing that enterprise object with a reference to the relationship using a fault, you save the expense of performing a fetch if it’s not necessary.

The default implementation of willRead checks to see if its receiver has already been fully initialized (that is, if its receiver is a full-formed enterprise object). If it hasn’t been fully initialized, it fills the object with values fetched from the database. Before an application attempts to invoke a method on a particular enterprise object, you must ensure that object has already fetched its data. To ensure that an enterprise object is in the correct state before its data is accessed, you need to invoke willRead, typically in “get” methods. (Enterprise objects don’t have to invoke willRead in “set” methods because the default implementation of willChange invokes willRead internally.)

Again, if you use EOGenericRecord subclasses that don’t access their properties with fields, willRead is handled for you by storedValueForKey.



< Previous PageNext Page > Hide TOC


Last updated: 2007-07-11




Did this document help you?
Yes: Tell us what works for you.

It’s good, but: Report typos, inaccuracies, and so forth.

It wasn’t helpful: Tell us what would have helped.
Get information on Apple products.
Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Copyright © 2007 Apple Inc.
All rights reserved. | Terms of use | Privacy Notice