Oracle TopLink Developer's Guide
10g Release 3 (10.1.3) B13593-01 |
|
![]() Previous |
![]() Next |
By default, when TopLink retrieves a persistent object, it retrieves all of the dependent objects to which it refers. When you enable indirection for an attribute mapped with a relationship mapping, TopLink uses an indirection object as a placeholder for the referenced object: TopLink defers reading the dependent object until you access that specific attribute. This can result in a significant performance improvement, especially if the application is interested only in the contents of the retrieved object rather than the objects to which it refers.
Oracle strongly recommends using indirection for all relationship mappings. Not only does this allow you to optimize data source access, but it also allows TopLink to optimize the unit of work processing, cache access, and concurrency.
Table 35-4 summarizes which mappings support this option.
Table 35-4 Mapping Support for Indirection
In general, Oracle recommends that you use value holder indirection (see "Value Holder Indirection") for one-to-one mappings and transparent indirect container indirection (see "Transparent Indirect Container Indirection") for collection mappings. Enable indirection for transformation mappings if the execution of the transformation is a resource-intensive task (such as accessing a database, in a relational project).
When using indirection with EJB, the version of EJB and application server you use affects how indirection is configured and what types of indirection are applicable (see "Indirection and EJB").
When using indirection with an object that your application serializes, you must consider the effect of any untriggered indirection objects at deserialization time (see "Indirection, Serialization, and Detachment").
For more information, see "Indirection".
To complete the indirection options on a mapping's General tab use this procedure:
Select the mapped attribute in the Navigator. Its properties appear in the Editor.
Click the General tab. The General tab appears.
Figure 35-2 General Tab, Indirection Options
Field | Description |
---|---|
Use Indirection | Specify if this mapping uses indirection. |
ValueHolder | Specify that the mapping uses Value Holder indirection. See "Value Holder Indirection" for more information. |
Proxy | Specify that the mapping uses Proxy indirection. See "Proxy Indirection" for more information. |
When creating mappings through the Java API, all foreign reference mappings default to using value-holder indirection and all transformation mappings default to not using indirection.
To disable indirection use ForeignReferenceMapping
method dontUseIndirection
.
To enable value holder indirection, use ForeignReferenceMapping
method useBasicIndirection
.
To enable transparent container indirection, use one of the following CollectionMapping
methods:
useTransparentCollection
useTransparentList
useTransparentMap
useTransparentSet
To enable proxy indirection, use ObjectReferenceMapping
method useProxyIndirection
.
This section provides additional information on the following:
Instances of oracle.toplink.mappings.ForeignReferenceMapping
and oracle.toplink.mappings.foundation.AbstractTransformationMapping
provide the useBasicIndirection
method to configure a mapping to an attribute that you code with an oracle.toplink.indirection.ValueHolderInterface
between it and the real object.
If the attribute is of a Collection
type (such as a Vector
), then you can either use an IndirectContainer
(see "Configuring IndirectContainer Indirection") or define the ValueHolder
in the constructor as follows:
addresses = new ValueHolder(new Vector());
Example 35-2 illustrates the Employee
class using ValueHolder
indirection. The class definition conceals the use of ValueHolder within the existing getter and setter methods.
Example 35-2 Class Using ValueHolder Indirection
public class Employee {
protected ValueHolderInterface address;
// Initialize ValueHolders in constructor
public Employee() {
address = new ValueHolder();
}
public Address getAddress() {
return (Address) this.addressHolder.getValue();
}
public void setAddress(Address address) {
this.addressHolder.setValue(address);
}
}
Example 35-3 shows how to configure a one-to-one mapping to the address
attribute.
Example 35-3 Mapping Using ValueHolder Indirection
OneToOneMapping mapping = new OneToOneMapping(); mapping.useBasicIndirection(); mapping.setReferenceClass(Employee.class); mapping.setAttributeName("address");
The application uses Employee
methods getAddress
and setAddress
to access the Address
object. Because basic indirection is enabled, TopLink expects the persistent fields to be of type ValueHolderInterface
.
If you are using ValueHolder
indirection with method accessing (see "Configuring Method Accessing"), in addition to changing your attributes types in your Java code to ValueHolderInterface
, you must also provide TopLink with two pairs of getter and setter methods:
getter and setter of the indirection object that are registered with the mapping and used only by TopLink. They include a get
method that returns an instance that conforms to ValueHolderInterface
, and a set
method that accepts one argument that conforms to the same interface.
getter and setter of the actual attribute value used by the application
Example 35-2 illustrates the Employee
class using ValueHolder
indirection with method access. The class definition is modified so that the address
attribute of Employee
is a ValueHolderInterface
instead of an Address
, and appropriate getter and setter methods are supplied.
Example 35-4 Class Using ValueHolder Indirection with Method Accessing
public class Employee { protected ValueHolderInterface address; // Initialize ValueHolders in constructor public Employee() { address = new ValueHolder(); } // getter and setter registered with the mapping and used only by TopLink public ValueHolderInterface getAddressHolder() { return address; } public void setAddressHolder(ValueHolderInterface holder) { address = holder; } // getter and setter methods used by the application to access the attribute public Address getAddress() { return (Address) address.getValue(); } public void setAddress(Address theAddress) { address.setValue(theAddress); } }
Example 35-3 shows how to configure a one-to-one mapping to the address
attribute.
Example 35-5 Mapping Using ValueHolder Indirection with Method Accessing
OneToOneMapping mapping = new OneToOneMapping(); mapping.useBasicIndirection(); mapping.setReferenceClass(Employee.class); mapping.setAttributeName("address"); mapping.setGetMethodName("getAddressHolder"); mapping.setSetMethodName("setAddressHolder");
The application uses Employee
methods getAddress
and setAddress
to access the Address
object. Because basic indirection is enabled, TopLink uses Employee
methods getAddressHolder
and setAddressHolder
methods when performing persistence operations on instances of Employee
.
When using indirection with EJB 3.0 CMP on OC4J (see "EJB 3.0 CMP and OC4J"), if your application serializes any indirection-enabled EJB (see "Indirection, Serialization, and Detachment"), then, to preserve untriggered indirection objects on deserialization, configure your client to use the same Java agent that OC4J uses, as follows:
Include the following JAR files (from <
TOPLINK_HOME
>\jlib
) in your client classpath:
toplink.jar
toplink-agent.jar
asm.jar
asm-util.jar
ejb3-toplink-session.xml
(and whatever project deployment XML it refers to)
Add the following argument to the Java command line you use to start your client:
-javaagent:toplink-agent.jar
Instances of oracle.toplink.mappings.ForeignReferenceMapping
and oracle.toplink.mappings.foundation.AbstractTransformationMapping
provide the useContainerIndirection
method to configure a mapping to an attribute that you code with an oracle.toplink.indirection.IndirectContainer
between it and the real object.
Using an IndirectContainer
, a java.util.Collection
class can act as a TopLink indirection object: the Collection
will only read its contents from the database when necessary (typically, when a Collection
accessor is invoked). Without an IndirectContainer
, all members of the Collection
must be retrieved when the Collection
attribute is accessed.
Example 35-2 illustrates the Employee
class using IndirectContainer
indirection with method access. The class definition is modified so that the addresses
attribute of Employee
is an IndirectContainer
instead of an Address
es, and appropriate getter and setter methods are supplied. In this example, addresses
is a java.util.List
, so an instance of oracle.toplink.indirection.IndirectList
is used.
Example 35-6 Class Using IndirectContainer Indirection
public class Employee { protected IndirectList addresses; // Initialize ValueHolders in constructor public Employee() { addresses = new IndirectList(); } // getter and setter methods registered with the mapping and used by TopLink public ValueHolderInterface getAddressesHolder() { return addresses.getValueHolder(); } public void setAddressesHolder(ValueHolderInterface holder) { addresses = addresses.setValueHolder(holder); } // getter and setter methods used by the application to access the attribute public List getAddresses() { return (List) addresses.getValue(); } public void setAddresses(List newAddresses) { addresses.removeAll(); addresses.addAll(newAddresses); } }
Example 35-3 shows how to configure a one-to-one mapping to the addresses
attribute.
Example 35-7 Mapping Using IndirectContainer Indirection
OneToOneMapping mapping = new OneToOneMapping(); mapping.useBasicIndirection(); mapping.setReferenceClass(Employee.class); mapping.setAttributeName("addresses"); mapping.setGetMethodName("getAddressesHolder"); mapping.setSetMethodName("setAddressesHolder");
Example 35-8 illustrates an Employee
to Address
one-to-one relationship.
Example 35-8 Proxy indirection Examples
public interface Employee { public String getName(); public Address getAddress(); public void setName(String value); public void setAddress(Address value); . . . } public class EmployeeImpl implements Employee { public String name; public Address address; . . . public Address getAddress() { return this.address; } public void setAddress(Address value) { this.address = value; } } public interface Address { public String getStreet(); public void setStreet(String value); . . . } public class AddressImpl implements Address { public String street; . . . }
In Example 35-8, both the EmployeeImpl
and the AddressImpl
classes implement public interfaces (Employee
and Address
respectively). Therefore, because the AddressImpl
class is the target of the one-to-one relationship, it is the only class that must implement an interface. However, if the EmployeeImpl
is ever to be the target of another one-to-one relationship using transparent indirection, it must also implement an interface, as shown in the following example:
Employee emp = (Employee) session.readObject(Employee.class); System.out.println(emp.toString()); System.out.println(emp.getAddress().toString()); // Would print: [Employee] John Smith { IndirectProxy: not instantiated } String street = emp.getAddress().getStreet(); // Triggers database read to get Address information System.out.println(emp.toString()); System.out.println(emp.getAddress().toString()); // Would print: [Employee] John Smith { [Address] 123 Main St. }
Using proxy indirection does not change how you instantiate your own domain objects for an insert operation. You still use the following code:
Employee emp = new EmployeeImpl("John Smith");
Address add = new AddressImpl("123 Main St.");
emp.setAddress(add);