Business Objects

The most important part of applications that use the Enterprise Object technology are the application’s enterprise object instances—your application’s business objects. Designing these objects is one of the primary tasks in developing an Enterprise Objects application. This chapter explains the structure of enterprise objects, how they interact with other parts of the technology, and how you can leverage the power of the technology in your custom enterprise object classes.

Designing enterprise objects entails three major steps:

This chapter briefly discusses the first two steps but leaves the details to EOModeler User Guide. This chapter focuses on the third step, implementing enterprise object classes. The chapter is divided into these sections:

Reference Entity

To help you better understand the concepts in this chapter, it helps to refer to a simple entity that includes characteristics that illustrate those concepts. This section describes that entity.

The entity is a simplified version of the Listing entity in the Real Estate model. (This example model is included with WebObjects 5.2. It is installed in /Developer/Examples/JavaWebObjects/Frameworks/JavaRealEstate.) It includes three attributes and one relationship:

This entity appears in EOModeler as shown in Figure 3-1.

Figure 2-1  A simplified version of the Listing entity
A simplified version of the Listing entityA simplified version of the Listing entity

Designing a Schema

If the application you’re building with Enterprise Objects accesses an existing database, its schema dictates many of the design decisions you’ll make when modeling enterprise objects. However, if you’re designing a database schema while designing enterprise objects, you have more flexibility and you can design a database schema that takes advantage of features of enterprise objects, such as faulting.

If you’re designing the database schema while you design enterprise objects, be sure to keep both designs in mind as you work; decisions you make about either design affect the other.

This chapter doesn’t directly address issues of database design, but the information presented here helps you create a design that works effectively with the Enterprise Objects frameworks.

Defining the Model

The work of writing enterprise objects typically begins in EOModeler. An individual enterprise object class maps to an entity in the data model. It then follows that an entity’s characteristics map to an enterprise object’s characteristics and behavior. You make the following decisions for a particular enterprise object in EOModeler:

Which Enterprise Object Class?

An enterprise object is an instance of a class that implements the com.webobjects.eocontrol.EOEnterpriseObject interface. This interface provides enterprise objects with the infrastructure they need to communicate with other enterprise objects, to set and return values for enterprise object attributes, and to provide a framework for validation and other business logic.

The Enterprise Objects frameworks provide for you two classes that each provide a complete implementation of the EOEnterpriseObject interface. These classes are com.webobjects.eocontrol.EOCustomObject and com.webobjects.eocontrol.EOGenericRecord. By default, enterprise object instances map to the implementation of the EOEnterpriseObject interface in the class EOGenericRecord. That is, entities you add to an EOModel are mapped to EOGenericRecord.

EOGenericRecord is sufficient for enterprise objects in which you don’t need to add custom business logic. But when you need to add custom business logic to a particular enterprise object class, you need to create a custom subclass of EOGenericRecord. To do this, you assign to an entity the name of a custom subclass and then generate source files for that custom class. This process is described in Generating Source Files.

When you assign an entity to a custom enterprise object class, the class file EOModeler generates for the custom class inherits from EOGenericRecord, but you can change the superclass to EOCustomObject. Most people prefer to work with enterprise object classes that inherit from EOGenericRecord because they are a bit easier to work with. There are at least a few reasons:

That said, there are a few reasons why you might want custom enterprise object classes to inherit directly from EOCustomObject rather than from EOGenericRecord. They include:

In short, the choice of which class to use is really a matter of preference. Both types of classes are first-class citizens within the Enterprise Objects frameworks, so you don’t lose any power or flexibility with either choice. If you’re new to the technology, you’ll probably find that EOGenericRecords are easier to work with because they encapsulate some of the framework’s complexities.

Implementing an Enterprise Object compares concrete examples of both types of classes.

Choosing Class Properties

When you add attributes to entities in EOModeler, they are marked as class properties (denoted by the diamond in an attribute’s row). When an attribute is marked as a class property, accessor methods for that attribute are included in that class definition when you generate source files for the class using EOModeler. When instances of an enterprise object are created, the data that maps to an attribute is fetched from the database and stored in the enterprise object instance, so you should mark attributes only as class properties if their data is needed in an enterprise object instance.

As a general rule, you should mark attributes as class properties only if their values are meaningful to a particular enterprise object’s business logic or if their values are displayed in the user interface. An attribute that won’t be used in business logic or that users don’t need to see or manipulate shouldn’t be marked as class properties.

Database artifacts such as primary and foreign keys shouldn’t be marked as class properties. There are exceptions to this rule, such as when you provide custom primary keys, but they are rare. Enterprise Objects handles key generation for you, so these keys aren’t necessary in your business logic classes. Also, it is rare that a primary key would contain data valuable to a user. (This generally reflects poor schema design, such as when a driver license number is used as a table’s primary key.)

In the Listing entity described in Reference Entity, the bedrooms, bathrooms, and address properties are selected as class properties, while the entity’s primary key, listingID, is not marked as a class property. The bedrooms, bathrooms, and address properties contain data that is meaningful to the application’s users; the listingID property does not.

A consideration, especially for advanced users, is to ensure that a given entity doesn’t have any redundant attributes that are marked as class properties. A common mistake when building data models that use entity inheritance is to mark multiple attributes as class properties when the attributes in fact represent the same data, such as can occur when flattening attributes and relationships. This issue is discussed in more detail in EOModeler User Guide.

Relationships With Other Enterprise Objects

One of the most important parts of a data model is the relationships it expresses between entities. You use relationships to access data across multiple objects in an object graph. Relationships are similar to attributes in that they can be marked as class properties. When they are marked as class properties, accessor methods are included in the generated class file for them, as is the case for attributes that are marked as class properties.

Unlike attributes, however, there are additional factors to consider when marking a relationship as a class property. Relationships in Enterprise Objects are not used just to specify the affinity between two tables. Relationships are also used when configuring entity inheritance and when flattening relationships (either explicitly or in a many-to-many relationship). Follow these guidelines when deciding whether to mark a relationship as a class property:

In the Listing entity described in Reference Entity, the address property, which represents the entity’s relationship to a ListingAddress entity, is marked as a class property. The relationship is a to-one relationship and joins on the source entity’s primary key listingID and the destination entity’s primary key listingID.

When you mark relationships as class properties, to-one relationships are represented as references to other objects and to-many relationships are represented as NSArray objects. You usually access the data in other objects by using relationship properties to traverse the object graph in running applications. For example, the following code uses Listing’s address relationship to access the street property in the Address object:

String streetAddress = (String)listing.valueForKeyPath(“address.street”);

Figure 3-2 illustrates this example.

The Listing enterprise object has many properties—many attributes and relationships. To access an attribute in an enterprise object, you invoke on it the method valueForKey(“attributeName”). To access an attribute in one of an enterprise object’s relationships, you invoke on it the method valueForKeyPath(“relationship.relationshipAttribute”). The methods valueForKey and valueForKeyPath return a java.lang.Object, which you usually cast to the data type of the attribute being accessed (such as java.lang.Number and java.lang.String, or NSArray if the attribute represents a to-many relationship in the enterprise object).

To access a Listing’s features, you use this code to get an array of a particular listing’s features:

NSArray features = (NSArray)listing.valueForKeyPath(“features”);
Figure 2-2  Key value-coding and relationships as key paths
Key value-coding and relationships as key paths

Enterprise Objects handles the resolution of relationships automatically, which may involve fetching from the database. You don’t have to worry about explicitly generating SQL statements or resolving joins; Enterprise Objects takes care of all this for you. All you need to supply is a well-designed data model and Enterprise Objects takes care of the rest.

Referential Integrity

Referential integrity rules are part of the definition of relationships between enterprise objects, just like joins between database tables. These rules work to maintain the integrity and consistency of the data that gets committed to the database. Enterprise Objects supports these referential integrity rules:

These rules are described in more detail in EOModeler User Guide. In most cases, you configure referential integrity rules in EOModeler. Your enterprise object classes have no direct knowledge of the referential integrity rules of their relationships. These rules are characteristics of EORelationship objects of which individual enterprise objects have no direct knowledge. This means that you need to be concerned with referential integrity rules when designing data models but not when adding custom logic to enterprise object classes.

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.

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 entitiesCustom 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.

Accessing an Enterprise Object’s Data

In implementing your enterprise object classes, you want to focus on the code that’s unique to your application, not on the code that deals with fitting your objects into the framework of Enterprise Objects. To help with this, Enterprise Objects provides key-value coding, a standard interface for accessing an enterprise object’s properties ( key-value coding was introduced in Key-Value Coding).

Key-value coding specifies that an object’s properties are accessed indirectly by name or key rather than directly through invocation of an accessor method or as fields. This provides a consistent way to access an object’s data, regardless if the object provides accessor methods or fields to its data. The EOEnterpriseObject interface implements the EOKeyValueCoding and NSKeyValueCoding interfaces, so custom enterprise object classes automatically inherit key-value coding behavior.

The basic methods for accessing an object’s values are takeValueForKey and valueForKey, which set or return the value for the specified key, respectively. Key-value coding uses the first accessor it finds when both setting and getting the value for a given key. The following lists describe the lookup order of accessors.

The order of lookup when retrieving a value is:

In the Listing entity described in Reference Entity, an invocation of valueForKey(“bedrooms”) on a Listing enterprise object looks up the value of the bedrooms property by invoking accessor methods and fields in the following order:

The order of lookup when setting a value is:

In the Listing entity described in Reference Entity, an invocation of takeValueForKey(new Integer(4),“bedrooms”) on a Listing enterprise object attempts to set the value of the bedrooms property by invoking accessor methods and setting fields in the following order:

There is another set of methods defined by the EOKeyValueCoding interface, takeStoredValueForKey and storedValueForKey. You never explicitly invoke these methods, but you may implement them in enterprise object classes. The stored value methods are used internally to transport data to and from trusted sources. For example, takeStoredValueForKey is used to initialize an object’s properties with values fetched from the database, whereas takeValueForKey is used to modify an object’s properties from values provided by a user.

The default lookup order for the stored value methods for retrieving the value of a property is:

The default lookup order for the stored value methods for setting the value of a property is:

Refer to the API reference for com.webobjects.eocontrol.EOKeyValueCoding and for com.webobjects.foundation.NSKeyValueCoding for complete information about accessor lookup order.

EOGenericRecord adds some additional behavior to key-value coding. When takeStoredValueForKey or storedValueForKey is invoked in an EOGenericRecord object, a corresponding willChange or willRead invocation is sent automatically, before the accessor is looked up. This supports the requirement that enterprise object instances notify other instances when they’re about to change the state of their data. This requirement is described in Change Notification.

Error Handling for Accessors

As introduced in Accessing an Enterprise Object’s Data, the public key-value coding accessors valueForKey and takeValueForKey have a default lookup order of accessors. The final lookup key for valueForKey is handleQueryWithUnboundKey("keyName") and for takeValueForKey is handleTakeValueForUnboundKey(value, "keyName"). That is, the default implementation of key-value coding invokes these methods when they receive a key for which they can find no accessor methods or fields. The default implementations throw exceptions, but you can override them to handle the error more gracefully.

Common Programming Errors

This section discusses some of the more subtle issues you need to be aware of when building enterprise object classes.

Overriding equals or hashcode

Don’t override equals or hashcode. Enterprise Objects uses the methods equals and hashcode in enterprise object classes to perform object comparisons and other functions. You should not implement either method in your enterprise object classes.

A common mistake is to use equals to compare enterprise objects. Avoid doing this and instead compare the global IDs of enterprise objects using equals. The code in Listing 3-3 provides an example.

Listing 2-3  Comparing two enterprise objects using equals on their global IDs

if ((editingContext.globalIDForObject(enterpriseObject1)).
equals(editingContext.globalIDForObject(enterpriseObject2)));

Immutable Primary Keys

Enterprise Objects does not support changeable primary keys. That is, you cannot change the value of a row’s primary key. A common, though poor, design pattern in database application development is to store meaningful business data as primary keys such as driver license numbers.

What if a value like this changes? If you try to change the value of a row’s primary key in Enterprise Objects, the enterprise object that represents that row will never be allowed to save in the Enterprise Objects application. The optimistic locking mechanism in Enterprise Objects always throws an exception if a row’s primary key changes. So, don’t mix columns that include meaningful business data with columns that are used to define database structure.

If you encounter a situation in which you must change an object’s primary key, the recommended procedure is to create a new enterprise object of the same type, copy the data from the original object into the new object, and delete the original object.

Primary Keys and Allows Null

In some cases, you need to set the allows null characteristic of a primary key attribute to true. This situation often occurs in master-detail relationships in which a detail object is added to the relationship but is not immediately saved to the database. You usually set a primary key’s allows null characteristic in the advanced attribute inspector in EOModeler.

What a primary key’s allows null characteristic means, however, may be unclear. No primary key is ever allowed to be null in a database that Enterprise Objects accesses. When you allow null for a primary key, validation within Enterprise Objects doesn’t throw an exception when a new enterprise object is created and is used within the application before being inserted into the database; on the way to the database, Enterprise Objects generates a value for the primary key regardless of its allows null characteristic.

Synchronizing Model Changes to Class Files

In the course of building an Enterprise Objects application, you’ll likely make changes to a model that affect enterprise object class files that you’ve generated from EOModeler, as described in Generating Source Files. Since you usually add custom logic to those enterprise object class files, you must make sure not to overwrite those changes when generating class files from an updated EOModel. EOModeler is careful to not blindly overwrite generated class files and instead presents a dialog allowing you to merge changes.

Clicking Merge in this dialog opens the FileMerge application and allows you to merge the updated entity definition class with the old entity definition class that contains custom business logic.

Instantiating Enterprise Objects

There are at least a few ways to create enterprise objects programmatically. The approach you use largely depends on the configuration of the entity from which you want to create the object.

In EOModeler, if the entity’s class is assigned to EOGenericRecord, you must first retrieve the entity’s class description before instantiating an enterprise object. An entity’s class description holds meta-information about an entity that describes an entity’s various characteristics, such as its attributes and relationships. Once you have an entity’s class description, you can instantiate an enterprise object of that class.

For example, consider the Listing entity described in Reference Entity. You can use this code to instantiate an enterprise object of type Listing:

EOClassDescription listingCD =           EOClassDescription.classDescriptionForEntityName("Listing");
EOEnterpriseObject listing =
         listingCD.createInstanceWithEditingContext(null, null);
editingContext.insertObject(listing);

Note that immediately after the enterprise object is created, it is inserted into an editing context. As a cardinal rule, all enterprise objects reside in an editing context. This is necessary in order for enterprise objects to send and receive the notifications necessary for change tracking and other mechanisms within Enterprise Objects. So, for every enterprise object you create, you must immediately insert it into an editing context.

You can simplify the above code example by supplying an editing context object as the first argument to the createInstanceWithEditingContext invocation.

If an entity’s class is assigned to a custom subclass of EOGenericRecord (so that the Listing entity’s class name assignment in EOModeler is “Listing” or “com.myapp.Listing”), you can also create an enterprise object with this code by using the subclasses’s constructor:

Listing image = new Listing();
editingContext.insertObject(image);

You can also use the method in the EOUtilities class called createAndInsertInstance to instantiate an enterprise object:

EOUtilities.createdAndInsertInstance(editingContext(), “Listing”);