Skip Headers
Oracle® Application Server TopLink Application Developer's Guide
10g Release 2 (10.1.2)
Part No. B15901-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

Using the OracleAS TopLink SDK

The OracleAS TopLink Software Development Kit (SDK) enables you to extend OracleAS TopLink to access objects stored on nonrelational data stores. To take advantage of the SDK, develop several classes that enable OracleAS TopLink to access your particular data store. You can take advantage of several OracleAS TopLink mappings and use different OracleAS TopLink customization features not used by applications that work with relational databases.

In OracleAS TopLink applications that address a relational database, a query works as follows:

  1. The client application builds a query.

  2. OracleAS TopLink converts the query search criteria into key-value pairs, formatted as a database row.

  3. OracleAS TopLink uses the key-value pairs to build a call to the relational database.

    OracleAS TopLink uses an internal mechanism to generate the calls, based on your chosen data repository. The SDK enables you to replace the internal mechanism with one of your own design. This enables you to develop custom calls that address nonrelational datasources.

There are four major steps to using the SDK:

Step One: Define an Accessor

OracleAS TopLink uses an accessor to maintain a connection to your data store. To define an accessor, create a subclass of SDKAccessor. The SDKAccessor is an implementation of the Accessor interface, which offers a minimal implementation, including:

  • The protocol required by the Accessor interface

  • Message logging

  • Non-JTS transaction support

  • Call execution

If you do not define your own accessor, the SDK creates an instance of oracle.toplink.sdk.SDKAccessor and uses it during execution.

Data Store Connection

When logging in, an OracleAS TopLink session uses your accessor to establish a connection to your data store by calling the connect(DatabaseLogin, Session) method.

The DatabaseLogin that is passed in holds several settings, including the user ID and password set by your application. As with regular database logins, you can store several user-defined properties in the DatabaseLogin that configure its connection. The API for this is:

void setProperty(String Object Value)

OracleAS TopLink occasionally queries the status of your connection accessor to your data store by calling the isConnected() method. This method returns true if the accessor still has a connection. You can set your accessor to verify the viability of the connection. This verification is optional if you know your data store will not drop the connection.

If your accessor connection times out or disconnects, your application can attempt to reconnect by calling the reestablishConnection(Session) method. Your application (rather than OracleAS TopLink) calls this method, which enables you to control when the application attempts to reconnect.

When logging out, an OracleAS TopLink session uses your accessor to disconnect from your data store by calling the disconnect(Session) method.

Call Execution

During execution of your application, the OracleAS TopLink session holds your accessor and uses it whenever you execute a call with the executeCall(Call, DatabaseRow, Session) method.

Transaction Processing

If you execute calls together within the context of a transaction, OracleAS TopLink indicates to your accessor that your connection must begin a transaction by calling the beginTransaction(Session) method. If any Exceptions occur during the execution of the calls contained within the transaction, OracleAS TopLink rolls back the transaction by calling rollbackTransaction(Session). If all the calls execute successfully, OracleAS TopLink commits the transaction by calling commitTransaction(Session).

Step Two: Create the Application Calls

OracleAS TopLink calls are the hooks OracleAS TopLink uses to call out to your code for reading and writing your nonrelational data. To write a call for the SDK, subclass oracle.toplink.sdk.AbstractSDKCall and implement the execute(DatabaseRow, Accessor) method.

The code for calls is specific to your particular data store. To see an example implementation of these calls, review the code for the XML calls in the package oracle.toplink.xml. "OracleAS TopLink XML Support" also discusses these calls.

A minimum implementation requires the following calls for every persistent Class stored in a nonrelational data store:

Depending on the capabilities of your data store, you may need to implement the following custom calls:

  • Named Session Call

  • Named Descriptor Call

If you use OracleAS TopLink relationship mappings, implement the appropriate calls to read the reference objects for each mapping.

You can divide any individual call into multiple calls, and combine the resulting calls into a single query.

Input Database Row

Calls include the key-value pairs that define the query. OracleAS TopLink formats this information into an input database row that implements the java.util.map interface. The input database row can also hold nested database rows or nested direct values. Allowing OracleAS TopLink to manipulate non-normalized, hierarchical data.

SDK Field Value

Use oracle.toplink.sdk.SDKFieldValue to manipulate nested database rows and direct values. Within the OracleAS TopLinkSDK, any field in a database row can have a value that is an instance of an SDK field value. An SDK field value can hold one or more nested database rows or direct values.

An SDK field value can also include a data type name indicating the type of elements held in the nested collection. The data store requirements for nested data elements determine whether the data type name is required.

Nested database rows can also themselves contain nested database rows, and there is no limit to the nesting.

Table 5-3 lists several examples in this chapter that illustrate the use of an SDK field value.

Table 5-3 SDK Field Value Examples

Example of an SDK Field Value to Reference
Read a single nested row Example 5-33
Write a single nested row Example 5-34
Read nested direct values Example 5-37
Write nested direct values Example 5-38
Read nested rows Example 5-43
Write nested rows Example 5-44

Read Object Call

A read object call reads the data required to build a single object for a specified primary key. OracleAS TopLink passes the search criteria to the ReadObject call as an input database row. The call returns a single database row for the specified object.

Read All Call

A read all call reads the data required to build a collection of all objects (instances) for a particular class. OracleAS TopLink passes an empty database row to the ReadAll call. The call returns a collection of all the database rows for the selected class.

Insert Call

An insert call inserts a newly created object on the appropriate data store. OracleAS TopLink passes values for all mapped fields for the inserted object as an input database row. The call returns a count of the number of rows inserted—usually one.

Update Call

An update call writes the data for a modified object to the appropriate data store. OracleAS TopLink passes the primary keys and values for all the mapped fields for the updated objects as an input database row. The call returns a count of the number of rows updated—usually one.

Delete Call

A delete call deletes the data from the data store based on primary key. OracleAS TopLink provides primary keys for the delete call as an input database row. The call returns a count of the number of rows deleted—usually one.

Does Exist Call

A does exist call checks for the existence of data for a specified primary key. This enables OracleAS TopLink to determine an insert or update call, depending on the result. OracleAS TopLink provides primary keys for the does exist call as an input database row. The call returns a null if the object does not exist on the data store, and a database row if the object does exist.

Custom Call

You can write a custom call to support other capabilities provided by your data store. Your custom calls can leverage parameter binding. Store custom calls as named queries in the OracleAS TopLink database session or in any OracleAS TopLink descriptor. Pass values to the calls as an input database row. The call returns whatever is appropriate for the containing query. Table 5-4 lists the query types and return values for Custom Calls.

Table 5-4 Query Types and Return Values for Custom Calls

Query Return value
DataModifyQuery Row count
DeleteAllQuery Row count
DeleteObjectQuery Row count
InsertObjectQuery Row count
UpdateObjectQuery Row count
DataReadQuery Vector of database rows
DirectReadQuery Vector of database rows
ValueReadQuery Vector of database rows
ReadAllQuery Vector of database rows
ReadObjectQuery Database row

FieldTranslator

If the names of fields expected by your OracleAS TopLink descriptors and database mappings differ from those generated by your data store (for example, when dealing with aggregate objects), you can resolve the mismatch by:

  • Subclassing the oracle.toplink.sdk.AbstractSDKCall. This enables you to use the SDKFieldTranslator class.

  • Building the SDKFieldTranslators into your own calls.

  • Creating your own mechanism for translating field names between OracleAS TopLink and your data store on a per-call basis.

Field Translator Interface

The oracle.toplink.sdk.FieldTranslator interface defines a simple read and write protocol for translating the field names in a database row. The default implementation of the oracle.toplink.sdk.DefaultFieldTranslator interface performs no translations.

oracle.toplink.sdk.SimpleyFieldTranslator

The oracle.toplink.sdk.SimpleFieldTranslator offers a mechanism for translating field names in a database row, either before the row is written to the data store, or after the row is read from the data store. SimpleFieldTranslator also allows for wrapping another FieldTranslator, and for processing the read and write translations through the wrapped FieldTranslator. A SimpleFieldTranslator also translates the field names of any nested database rows contained in SDK field values.

Example 5-24 Building a SimpleFieldTranslator

/* Add translations for the first and last name field names. F_NAME on the data store will be converted to FIRST_NAME for OracleAS TopLink, and vice versa. Likewise for L_NAME and LAST_NAME. */
AbstractSDKCall call = new EmployeeCall();
SimpleFieldTranslator translator = new SimpleFieldTranslator();
translator.addReadTranslation("F_NAME", "FIRST_NAME");
translator.addReadTranslation("L_NAME", "LAST_NAME");
call.setFieldTranslator(translator);

AbstractSDKCall offers methods that enable you to perform the same operation, without building your own translator.

AbstractSDKCall call = new EmployeeCall();
call.addReadTranslation("F_NAME", "FIRST_NAME");
call.addReadTranslation("L_NAME", "LAST_NAME");

If your calls are all subclasses of AbstractSDKCall, use the method in SDKDescriptor that sets the same field translations for all the calls in the DescriptorQueryManager, as follows:

descriptor.addReadTranslation("F_NAME", "FIRST_NAME");
descriptor.addReadTranslation("L_NAME", "LAST_NAME");

SDKDataStoreException

If your call encounters a problem while accessing your data store, it raises an oracle.toplink.sdk.SDKDataStoreException. This exception can hold an error code, a session, an internal exception, a database query, and an accessor. An exception handler can use this state to recover from the thrown exception or to provide useful information to the user or developer about the cause of the exception.

Step Three: Build Descriptors and Mappings

You can use your developed calls to define the descriptors and mappings. OracleAS TopLink can use these descriptors and mappings to read and write your objects rather than the normal OracleAS TopLink descriptors. Use a subclass of the descriptor, oracle.toplink.sdk.SDKDescriptor. This class provides support for mappings supplied by the SDK. The SDK supports most of the typical OracleAS TopLink mappings, as well as the mappings that provide access to non-normalized data.

SDK Descriptor

The SDK supports most of the properties of the standard descriptor, including:

For more information about other properties, see "Other Supported Properties" and "Unsupported properties".

Basic Properties

The code required to build a basic SDKDescriptor is almost identical to that used to build a normal descriptor.

Example 5-25 A Basic SDK Descriptor

SDKDescriptor descriptor = new SDKDescriptor();
descriptor.setJavaClass(Employee.class);
descriptor.setTableName("employee");
descriptor.setPrimaryKeyFieldName("id");

The Java class is required. The table name is usually required. How you store the data and translate the calls determines whether you allow multiple table names. OracleAS TopLink also requires the primary key field name, which OracleAS TopLink uses to maintain object identity.

Descriptor Query Manager

The major difference between building an SDKDescriptor and building a standard descriptor is that you define all the custom queries for the descriptor query manager.

Example 5-26 Building a Database Query for the Descriptor Query Manager

ReadObjectQuery query = new ReadObjectQuery();
    query.setCall(new EmployeeReadCall());
    descriptor.getQueryManager().setReadObjectQuery(query);

SDKDescriptor has several convenience methods that simplify setting all these calls.

descriptor.setReadObjectCall(new EmployeeReadCall());
descriptor.setReadAllCall(new EmployeeReadAllCall());

descriptor.setInsertCall(new EmployeeInsertCall());
descriptor.setUpdateCall(new EmployeeUpdateCall());
descriptor.setDeleteCall(new EmployeeDeleteCall());

descriptor.setDoesExistCall(new EmployeeDoesExistCall());

You can also create custom calls to an SDKDescriptor that enable you to set query criteria at runtime.

Example 5-27 A Dynamic Query

// "LastName" is an argument for the call
descriptor.addReadAllCall("readByLastName", new EmployeesByLastNameCall(), "LastName");
// "Location" is an argument for the call 
descriptor.addReadObjectCall("readByLocation", new EmployeeByLocationCall(), "Location");

Your application invokes custom calls at runtime and provides a parameter value through a database row. The call communicates with your data store and returns a database row with the appropriate data to build an instance of the returned object.

Sequence Numbers

If your data store provides support for sequencing, you can configure your descriptor to use sequence numbers.

Example 5-28 Using Sequencing

descriptor.setSequenceNumberName("employee");
descriptor.setSequenceNumberFieldName("id");

To use sequencing, define several custom queries that query and update the sequence numbers.

For more information, see the Oracle Application Server TopLink API Reference.

Inheritance

The SDKDescriptor supports OracleAS TopLink inheritance settings. If you define a single table in the root class descriptor, but do not define any additional tables in the subclass descriptors, calls build database rows for a single table, leaving out the fields that are not required for the particular subclass descriptor.

For more information, see "Inheritance".

Other Supported Properties

The SDKDescriptor supports most other descriptor properties without any special consideration, including:

  • Interfaces

  • Copy Policy

  • Instantiation Policy

  • Wrapper Policy

  • Identity Maps

  • Descriptor Events

Unsupported properties

The OracleAS TopLink SDK does not support the following descriptor properties:

  • Query Keys

  • Optimistic Locking

Standard Mappings

The OracleAS TopLink SDK provides support for many of the database mappings in the base OracleAS TopLink class library, as well as hierarchical data mechanisms.

Direct Mappings

The OracleAS TopLink SDK supports all the base OracleAS TopLink direct mappings:

  • Direct-to-field mappings

  • Type conversion mappings

  • Object type mappings

  • Serialized object mappings

  • Transformation mappings

The only mapping that requires special consideration is the SerializedObjectMapping. Read calls that support descriptors with this type of mapping must return the data for the SerializedObjectMapping either as a byte array (byte[]) or as a hexadecimal string representation of a byte array. OracleAS TopLink passes the data for the SerializedObjectMapping to any Write call as a byte array (byte[]).

Relationship mappings

The OracleAS TopLink SDK offers support for several of the base OracleAS TopLink relationship mappings. In addition, alternative mappings provide any functionality lost by unsupported mappings in the SDK.

Private relationships

The OracleAS TopLink SDK offers full support for private relationships. When you write an object to the data store, OracleAS TopLink also writes its private objects. Likewise, when you remove an object, OracleAS TopLink also removes its private objects.

Because OracleAS TopLink invokes the appropriate calls to write and delete private objects, your calls do not need to be aware of private relationships. OracleAS TopLink acquires the appropriate call for a particular private object from the DescriptorQueryManager of the object.

Indirection

The OracleAS TopLink SDK provides full support for OracleAS TopLink indirection, including valueholder indirection, proxy indirection, and transparent indirection.

For more information, see "Indirection".

Because OracleAS TopLink invokes calls to read in reference objects when required, your calls do not need to be aware of indirection. OracleAS TopLink acquires the appropriate call for indirect relationships from the custom selection query from the relationship mapping.

Container Policy

The OracleAS TopLink SDK supports OracleAS TopLink container policies. A container policy allows you to specify the concrete class OracleAS TopLink uses to store query results.

Calls do not need to be aware of the container policy. For ease of development, specify your calls to use a java.util.Vector to return collections of database rows. OracleAS TopLink converts any vector of database rows into the appropriate collection (or map) of business objects. OracleAS TopLink determines the appropriate concrete container class by getting the container policy from the appropriate database query or database mapping.

Aggregate Object Mapping

Although the limitations of the aggregate object mapping prevent the OracleAS TopLink SDK from supporting it, the SDK does provide nearly equivalent behavior. See "SDK Aggregate Object Mapping" .

One-To-One Mapping

The OracleAS TopLink SDK supports one-to-one mapping. Provide the mapping with a custom selection query as follows:

ReadObjectQuery query = new ReadObjectQuery();
query.setCall(new ReadAddressForEmployeeCall());
mapping.setCustomSelectionQuery(query);

The Read call used for the custom selection query must be aware of whether the mapping uses either a source foreign key or a target foreign key. It must also know which fields hold the primary or foreign key values. Because the mapping contains this information, construct the call with the mapping as a parameter, as follows:

query.setCall(new ReadAddressForEmployeeCall(mapping));

Variable-One-To-One Mapping

The OracleAS TopLink SDK supports variable one-to-one mapping. As with the one-to-one mapping, you must provide the mapping with a custom selection query.

Direct Collection Mapping

The OracleAS TopLink SDK supports direct collection mapping. Use a direct collection mapping if your data store requires that you perform an additional query to fetch the direct values related to a given object. If your data store includes the direct values in a hierarchical fashion within the database row for a given object, use SDK direct collection mapping.

For more information about SDK direct collection mapping, see "SDK Direct Collection Mapping".

Provide the direct collection mapping with several custom queries. Because the objects contained in a direct collection do not have a descriptor, provide the mapping with the queries that OracleAS TopLink uses to insert and delete the reference objects.

Example 5-29 Mappings and Custom Selection Queries for Direct Collection Mapping

DirectReadQuery readQuery = new DirectReadQuery();
readQuery.setCall(new ReadResponsibilitiesForEmployeeCall());
mapping.setCustomSelectionQuery(readQuery);

DataModifyQuery insertQuery = new DataModifyQuery();
insertQuery.setCall(new InsertResponsibilityForEmployeeCall());
mapping.setCustomInsertQuery(insertQuery);

DataModifyQuery deleteAllQuery = new DataModifyQuery();
deleteAllQuery.setCall(new DeleteResponsibilitiesForEmployeeCall());
mapping.setCustomDeleteAllQuery(deleteAllQuery);

The mapping does not need a custom update query because, if any of the reference objects change, OracleAS TopLink deletes and reinserts them.

The Read and Delete calls for this mapping must know which fields hold the primary key values. Because the mapping contains this information, construct the call with the mapping as a parameter, as follows:

readQuery.setCall(new ReadResponsibilitiesForEmployeeCall(mapping));
deleteAllQuery.setCall(new DeleteResponsibilitiesForEmployeeCall(mapping));

One-To-Many Mapping

The OracleAS TopLink SDK supports one-to-many mapping. Use a one-to-many mapping if the reference objects have foreign keys to the source object (target foreign keys). However, if the foreign keys are forward-pointing (source foreign keys) and are included in a hierarchical fashion in the database row for a given object, use SDK aggregate object mapping instead.

For more information about SDK aggregate object mapping, see "SDK Aggregate Object Mapping".

Example 5-30 Mappings and Custom Selection Queries for One-To-Many Mapping

ReadAllQuery readQuery = new ReadAllQuery();
readQuery.setCall(new ReadManagedEmployeesForEmployeeCall());
mapping.setCustomSelectionQuery(readQuery);

You can also provide the mapping with a custom DeleteAll query. If this query is present, OracleAS TopLink uses it to delete all components in the relationship with a single query. Without this query, OracleAS TopLink deletes components individually.

Example 5-31 Defining a Delete All Query

DeleteAllQuery deleteAllQuery = new DeleteAllQuery();
deleteAllQuery.setCall(new DeleteManagedEmployeesForEmployeeCall());
mapping.setCustomDeleteAllQuery(deleteAllQuery);

The Read and Delete calls for this mapping must know which fields hold the primary key values. Because the mapping contains this information, construct the call with the mapping as a parameter, as follows:

readQuery.setCall(new ReadManagedEmployeesForEmployeeCall(mapping));
deleteAllQuery.setCall(new DeleteManagedEmployeesForEmployeeCall(mapping));

Aggregate Collection Mapping

The OracleAS TopLink SDK supports aggregate collection mapping. Aggregate collection mapping is similar to the one-to-many mapping but does not require a back reference mapping from each of the target objects to the source object.

As with the one-to-many mapping, supply the mapping with a custom selection query. You can also include a DeleteAll query.

Many-To-Many Mapping

The OracleAS TopLink SDK does not support many-to-many mapping because it depends on the relational implementation of many-to-many relationships.

Structure Mapping

The OracleAS TopLink SDK does not support structure mapping because it depends on the object-relational data model. However, the SDK aggregate object mapping provides nearly identical functionality.

For more information, see "SDK Aggregate Object Mapping".

Reference Mapping

The OracleAS TopLink SDK does not support reference mapping because it depends on the object-relational data model. However, OneToOne mapping provides nearly identical functionality.

For more information, see "One-To-One Mapping").

Array Mapping

The OracleAS TopLink SDK does not support array mapping because it depends on the object-relational data model. However, SDK direct collection mapping provides nearly identical functionality.

For more information, see "SDK Direct Collection Mapping" .

Object Array Mapping

The OracleAS TopLink SDK does not support object array mapping because it depends on the object-relational data model. However, SDK direct collection mapping provides nearly identical functionality. For more information, see "SDK Direct Collection Mapping" .

Nested Table Mapping

The OracleAS TopLink SDK does not support nested table mapping because it depends on the object-relational data model. However, SDK object collection mapping provides nearly identical functionality.

For more information, see "SDK Object Collection Mapping" .

SDK Mappings

The OracleAS TopLink SDK provides four new mappings that support non-normalized, hierarchical data:

SDK Aggregate Object Mapping

The SDK aggregate object mapping is similar to the standard aggregate object mapping, but differs as follows:

  • All fields that the reference (aggregate) descriptor uses to build the aggregate object appear in a single, nested database row, not in the base database row. The base database row has a single field mapped to the aggregate object attribute that contains an SDK field value. This SDK field value holds the nested database row, and this nested database row contains all the fields needed by the reference descriptor to build an instance of the aggregate object.

  • There is no need for field name translations. If necessary, the appropriate call can translate the field names when it converts data from the data store native format to an OracleAS TopLink database row (and the reverse), as described in "FieldTranslator" .

  • There is no need for the isNullAllowed flag. Because the fields used to build the aggregate object appear in a single field in the base database row, there is no need to specify how to handle null field values. If the attribute is null, then the field value in the base database row is also null. If the attribute contains an instance of the aggregate object with all null attributes, then the field value in the base database row is an SDK field value with a single, nested database row whose field values are all null.

The code to build an SDK aggregate object mapping is similar to that for the aggregate object mapping. Specify an attribute name, a reference class, and a field name.

Example 5-32 Building an SDK Aggregate Object Mapping

SDKAggregateObjectMapping mapping = new SDKAggregateObjectMapping();
mapping.setAttributeName("period");
mapping.setReferenceClass(EmploymentPeriod.class);
mapping.setFieldName("period");
descriptor.addMapping(mapping);

Because the data used to build the aggregate object appears nested within the base database row, a separate query is not necessary to fetch the data for the aggregate object. Table 5-5 illustrates an example of the values contained in a typical database row with data for an aggregate object.

Table 5-5 Field Names and Mappings for SDK Aggregate Object Mapping

Field Name Field Value
employee.id
1
employee.firstName
"Grace"
employee.lastName
"Hopper"
employee.period
SDKFieldValue
elements=[
    DatabaseRow(
       employmentPeriod.startDate="1943-01  -01"
      employmentPeriod.endDate="1992-01-01"
    )
]
elementDataTypeName="employmentPeriod"
isDirectCollection=false

In the Example 5-33, an SDK aggregate object mapping maps the attribute period to the field employee.period and specifies the reference class as EmploymentPeriod. The value in the field employee.period is an SDK field value with a single, nested database row. The EmploymentPeriod descriptor uses this nested row to build the aggregate object.

The names of the fields in the nested database row must match those expected by the EmploymentPeriod descriptor.

Example 5-33 Reading for an SDK Aggregate Object Mapping

Integer id = (Integer) row.get("employee.id");
String firstName = (String) row.get("employee.firstName");
String lastName = (String) row.get("employee.lastName");

SDKFieldValue value = (SDKFieldValue) row.get("employee.period");
DatabaseRow nestedRow = (DatabaseRow) value.getElements().firstElement();
String startDate = (String) nestedRow.get("employmentPeriod.startDate");
String endDate = (String) nestedRow.get("employmentPeriod.endDate");

Example 5-34 Write Call that Supports SDK Aggregate Object Mapping

DatabaseRow row = new DatabaseRow();
row.put("employee.id", new Integer(1));
row.put("employee.firstName", "Grace");
row.put("employee.lastName", "Hopper");

DatabaseRow nestedRow = new DatabaseRow();
nestedRow.put("employmentPeriod.startDate", "1943-01-01");
nestedRow.put("employmentPeriod.endDate", "1992-01-01");
Vector elements = new Vector();
elements.addElement(nestedRow);

SDKFieldValue value = SDKFieldValue.forDatabaseRows(elements,"employmentPeriod");
row.put("employee.period", value);

SDK Direct Collection Mapping

The SDK direct collection mapping is similar to the standard direct collection mapping because it represents a collection of objects that are not OracleAS TopLink-enabled (they are not associated with any OracleAS TopLink descriptors).

The SDK direct collection mapping differs from a direct collection mapping because the data representing the collection of objects appears nested within the base database row. Because of this, a separate query to the data store is not necessary to read the data.

To build an SDK direct collection mapping, specify the attribute and the field names. Alternatively, if your data store requires you to indicate the data type name of each element in the direct collection, include the data type name instead.

Example 5-35 Building an SDK Direct Collection Mapping

SDKDirectCollectionMapping mapping = new SDKDirectCollectionMapping();
mapping.setAttributeName("responsibilitiesList");
mapping.setFieldName("responsibilities");
mapping.setElementDataTypeName("responsibility");
descriptor.addMapping(mapping);

The SDK direct collection mapping container policy enables you to specify the concrete implementation of the Collection interface that holds the direct collection, as follows:

mapping.useCollectionClass(Stack.class);

The SDK direct collection mapping also allows you to specify the class of objects in the direct collection or the database row. If possible, OracleAS TopLink converts the objects contained by the direct collection before setting the attribute in the object or passing the collection to your call.

Example 5-36 Specifying Object Types for an SDK Direct Collection Mapping

mapping.setAttributeElementClass(Class.class);
mapping.setFieldElementClass(String.class);

Because the data used to build the aggregate object appears nested within the base database row, a separate query is not necessary to fetch the data for the SDK direct collection mapping.

Table 5-6 illustrates examples of the values that appear in a typical database row with data for a direct collection.

Table 5-6 Field Names and Values for SDK Aggregate Object Mapping

Field Name Field Value
employee.id 1
employee.firstName "Grace"
employee.lastName "Hopper"
employee. responsibilities
SDKFieldValue
elements=[
    "find bugs"(
    "develop compilers"
]
elementDataTypeName="responsibility"
isDirectCollection=true

In Example 5-37, an SDK direct collection mapping maps the attribute responsibilitiesList to the field employee.responsibilities. The value in the field employee.responsibilities is an SDK field value that contains a collection of strings that make up the direct collection.

Example 5-37 Reading for an SDK Direct Collection Mapping

DatabaseRow row = new DatabaseRow();
row.put("employee.id", new Integer(1));
row.put("employee.firstName", "Grace");
row.put("employee.lastName", "Hopper");

Vector responsibilities = new Vector();
responsibilities.addElement("find bugs");
responsibilities.addElement("develop compilers");
SDKFieldValue value = SDKFieldValue.forDirectValues(responsibilities, "responsibility");
row.put("employee.responsibilities", value);

Example 5-38 Write Call that Supports an SDK Direct Collection Mapping

Integer id = (Integer) row.get("employee.id");
String firstName = (String) row.get("employee.firstName");
String lastName = (String) row.get("employee.lastName");

SDKFieldValue value = (SDKFieldValue) row.get("employee.responsibilities");
Vector responsibilities = value.getElements();

SDK Aggregate Collection Mapping

The SDK aggregate collection mapping maps attributes that are collections of aggregate objects constructed from data contained in the base database row.

The data that the reference (aggregate) descriptor uses to build the aggregate collection appears in a collection of nested database rows, not in the base database row. The base database row has a single field mapped to the aggregate collection attribute that contains an SDK field value. This SDK field value holds the nested database rows, and the nested database rows each contain all the fields that the reference descriptor requires to build a single element in the aggregate collection.

To build an SDK aggregate collection mapping, specify an attribute name, a reference class, and a field name.

Example 5-39 Building an SDK Aggregate Collection Mapping

SDKAggregateCollectionMapping mapping = new SDKAggregateCollectionMapping();
mapping.setAttributeName("phoneNumbers");
mapping.setReferenceClass(PhoneNumber.class);
mapping.setFieldName("phoneNumbers");
descriptor.addMapping(mapping);

The SDK aggregate collection mapping container policy enables you to specify the concrete implementation of the Collection interface that holds the direct collection.

mapping.useCollectionClass(Stack.class);

Because the data used to build the aggregate collection is already nested within the base database row, it does not require a separate query to fetch the data.

Table 5-7 illustrates examples of the values that appear in a typical database row with data for an aggregate collection.

Table 5-7 Field names and values for SDK Aggregate Collection Mapping

Field Name Field Value
employee.id 1
employee.firstName ÒGraceÓ
employee.lastName ÒHopperÓ
employee. phoneNumbers
SDKFieldValue
elements=[ 
    DatabaseRow(
        phone.areaCode="888"
        phone.number="555-1212"
        phone.type="work"
    )
    DatabaseRow(
        phone.areaCode="800"
        phone.number="555-1212"
        phone.type="home"
    )aggregate collection mapping
]
elementDataTypeName="phone"
isDirectCollection=false

In Example 5-40, an SDK aggregate collection mapping maps the attribute phoneNumbers to the field employee.phoneNumbers and specifies the reference class as phoneNumber. The value in the field employee.phoneNumbers is an SDK field value with a collection of nested database rows. The PhoneNumber descriptor uses these nested rows to build the elements of the aggregate collection. The names of the fields in the nested database rows must match those expected by the PhoneNumber descriptor.

Example 5-40 Reading for an SDK Aggregate Collection Mapping

Integer id = (Integer) row.get("employee.id");
String firstName = (String) row.get("employee.firstName");
String lastName = (String) row.get("employee.lastName");

SDKFieldValue value = (SDKFieldValue) row.get("employee.phoneNumbers");
Enumeration enum = value.getElements().elements();
while (enum.hasMoreElements()) {DatabaseRow nestedRow = (DatabaseRow) enum.nextElement();
String areaCode = (String) nestedRow.get("phone.areaCode");
String number = (String) nestedRow.get("phone.number");
String type = (String) nestedRow.get("phone.type");
...
}

Example 5-41 Write Call that Supports an SDK Aggregate Collection Mapping

DatabaseRow row = new DatabaseRow();
row.put("employee.id", new Integer(1));

row.put("employee.firstName", "Grace");
row.put("employee.lastName", "Hopper");

Vector elements = new Vector();

DatabaseRow nestedRow1 = new DatabaseRow();
nestedRow1.put("phone.areaCode", "888");
nestedRow1.put("phone.number", "555-1212");
nestedRow1.put("phone.type", "work");
elements.addElement(nestedRow1);

Database nestedRow2 = new DatabaseRow();
nestedRow2.put("phone.areaCode", "800");
nestedRow2.put("phone.number", "555-1212");
nestedRow2.put("phone.type", "home");
elements.addElement(nestedRow2);

SDKFieldValue value = SDKFieldValue.forDatabaseRows(elements, "phone");
row.put("employee.phoneNumbers", value);

SDK Object Collection Mapping

The SDK object collection mapping is similar to the standard one-to-many mapping—because both map a collection of target objects that use foreign keys to point to their primary keys. However, the foreign keys in SDK object collection mapping appear in the base database row that references the target objects' primary keys. This makes the foreign keys in an SDK object collection mapping forward-pointing, whereas the foreign keys in a one-to-many mapping are back-pointing.

All foreign keys appear in a collection of nested database rows, not in the base database row. The base database row includes a single field, mapped to the object collection attribute containing an SDK field value. This SDK field value holds the nested database rows, and these nested database rows each contain the fields required to build a foreign key to the primary key of an element object.

The code to build an SDK object collection mapping is similar to that for the one-to-many mapping. Specify an attribute name, a reference class, a field name, and the source foreign and target key relationships.

If your data store requires you to indicate the data type name of each element in the collection of foreign keys, include the data type name. Alternatively, you can provide this information with your call. Build a custom selection query to read the reference objects contained in the collection.

Example 5-42 Building an SDK Object Collection Mapping

SDKObjectCollectionMapping mapping = new SDKObjectCollectionMapping();
mapping.setAttributeName("projects");
mapping.setReferenceClass(Project.class);
mapping.setFieldName("projects");
mapping.setSourceForeignKeyFieldName("projectId");
mapping.setReferenceDataTypeName("project"); 
descriptor.addMapping(mapping);

The SDK object collection mapping container policy allows you to specify the concrete implementation of the Collection interface that holds the collection of objects.

mapping.useCollectionClass(Stack.class);

Table 5-8 demonstrates an example of the values contained in a typical database row with data for a collection of foreign keys.

Table 5-8 Field Names and Values for SDK Object Collection Mapping

Field Name Field Value
employee.id 1
employee.firstName ÒGraceÓ
employee.lastName ÒHopperÓ
employee.projects
SDKFieldValue
elements=[ 
    DatabaseRow(
        project.projectId=42
    )
    DatabaseRow(
        project.projectid=17
    )
]
elementDataTypeName="project"
isDirectCollection=false

Example 5-43 illustrates an SDK object collection mapping that maps the attribute projects to the field employee.projects and specifies the reference class as Project. The value in the field employee.projects is an SDK field value with a collection of nested database rows.

Nested rows contain foreign keys that the custom selection query of the mapping uses to read in the elements of the object collection. The field names in the nested database rows must match those expected by the call of the custom selection query.

Example 5-43 Reading for an SDK Object Collection Mapping

DatabaseRow row = new DatabaseRow();
row.put("employee.id", new Integer(1));
row.put("employee.firstName", "Grace");
row.put("employee.lastName", "Hopper");

Vector elements = new Vector();

DatabaseRow nestedRow1 = new DatabaseRow();
nestedRow1.put("project.projectId", new Integer(42));
elements.addElement(nestedRow1);

DatabaseRow nestedRow2 = new DatabaseRow();
nestedRow2.put("project.projectId", new Integer(17));
elements.addElement(nestedRow2);

SDKFieldValue value = SDKFieldValue.forDatabaseRows(elements, "project");
row.put("employee.projects", value);

Example 5-44 Write Call that Supports an SDK Object Collection Mapping

Integer id = (Integer) row.get("employee.id");
String firstName = (String) row.get("employee.firstName");
String lastName = (String) row.get("employee.lastName");

SDKFieldValue value = (SDKFieldValue row.get("employee.projects");
Enumeration enum = value.getElements().elements();
while (enum.hasMoreElements(DatabaseRow nestedRow = (DatabaseRow)enum.nextElement();
Object projectId = nestedRow.get("project.projectId");
// do stuff with the foreign key
}

Step Four: Deploy the Application Using Sessions

After you develop your accessor and calls, and map your object model to your data store, you can configure and log in to a database session, following these steps:

  • If necessary, build an instance of your custom platform.

  • If necessary, build an instance of an SDK login, with this custom platform.

  • Build an OracleAS TopLink project with this SDK login, populating it with your descriptors.

  • Acquire a session from this OracleAS TopLink project and log in.

    For more information about acquiring a session, see "Session Manager".

SDK Platform and Sequencing

OracleAS TopLink uses the platform classes to isolate the database platform-specific implementations of two major activities:

  • SQL generation

  • Sequence number generation

Because the OracleAS TopLink SDK is generally unconcerned with SQL generation, you usually build a custom platform only if your data store provides a mechanism for generating sequence numbers. In this case, create your subclass, and override the appropriate methods for building the calls that read and update sequence numbers.

If you use sequence numbers and want OracleAS TopLink to manage them for you, create a subclass of oracle.toplink.sdk.SDKPlatform.

Use the buildSelectSequenceCall() method to build and call the sequence number Read call. OracleAS TopLink invokes this call to read the value of a specific sequence number. The database row in the call contains the sequenceNameFieldName (as set in the SDK login), and the field value is the name of the sequence number returned by the call.

The buildUpdateSequenceCall() method builds the sequence number Update call. OracleAS TopLink invokes this call to update the value of a specific sequence number. The database row in the call contains two fields:

  • The first field name is the sequenceNameFieldName (as set in the SDK login); the field value is the name of the sequence number updated by the call.

  • The second field name is the sequenceCounterFieldName (as set in the SDK login); the field value is the new value for the sequence number identified by the first field.

SDK Login

If you build a custom SDK platform, use it to construct and configure your SDK login.

SDKLogin login = new SDKLogin(new EmployeePlatform());

If you do not require a custom platform, use the default constructor for SDK login.

SDKLogin login = new SDKLogin();

If you use a custom accessor to maintain a connection to your data store, configure the login to use it. Doing this enables OracleAS TopLink to construct a new instance of your accessor when the application requires a connection to the data store. If you do not use a custom accessor, you do not need to set this property. In that case, the login uses the SDKAccessor class by default.

login.setAccessorClass(EmployeeAccessor.class);

You can then configure the values of the standard login properties.

login.setUserName("user");
login.setPassword("password");

login.setSequenceTableName("sequence");
login.setSequenceNameFieldName("name");
login.setSequenceCounterFieldName("count");

You can store non-OracleAS TopLink properties in the login. Your custom accessor uses these properties when it connects to the data store.

login.setProperty("foo", aFoo);
Foo anotherFoo = (Foo) login.getProperty("foo");

OracleAS TopLink Project

Build your OracleAS TopLink project by creating an instance of oracle.toplink.sessions.Project, and passing it your login. You can then add your descriptors to the project.

Example 5-45 Instantiating the Project and Adding Descriptors

Project project = new Project(login);
project.addDescriptor(buildEmployeeDescriptor());
project.addDescriptor(buildAddressDescriptor());
project.addDescriptor(buildProjectDescriptor());
// etc.

Session

After you build your OracleAS TopLink project, obtain a database session (or server session) and log in.

Example 5-46 Obtaining a Session and Login

DatabaseSession session = project.createDatabaseSession();
session.login();

When you finish with the session, log out.

session.logout();

Unsupported Features

The OracleAS TopLink SDK does not offer support for the following regular OracleAS TopLink features:

  • Expressions

  • Pessimistic locking

  • Cursored streams and scrollable cursors