Skip Headers

Oracle Application Server TopLink Application Developer's Guide
10g (9.0.4)

Part Number B10313-01
Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Go to previous page Go to next page

5
Data Access

Managing and protecting data are key components of good application design. Oracle Application Server TopLink enables you to build your application around your choice of datasource and connection, and to customize data access functions to improve performance and security.

This chapter explores the ways you can configure OracleAS TopLink data access, and includes discussions on:

Introduction to Data Access Concepts

In OracleAS TopLink applications, data access offers the functionality and features that enable you to manipulate data on a database and mapping a data source with the OracleAS TopLink Software Development Kit (SDK).

This section introduces some of the key concepts associated with OracleAS TopLink data access features.

JDBC Connections

Java Database Connectivity (JDBC) is an application programming interface (API) that gives Java applications access to a database. OracleAS TopLink applications rely on JDBC connections to read objects from, and write objects to, the database.

OracleAS TopLink applications use either individual JDBC connections or a JDBC connection pool, depending on the application architecture.

Individual JDBC Connections

An individual JDBC connection gives a single user access to the database for a single session. For example, a two-tier OracleAS TopLink architecture generally connects to the database using a database session and a single JDBC connection. OracleAS TopLink invokes the JDBC connection as part of the login for the database session in the sessions.xml file.

For more information about sessions, see Chapter 4, "Sessions".

JDBC Connection Pools

A JDBC connection pool is a collection of JDBC connections managed as a group. Most three-tier multiuser applications use connection pools.

JDBC connection pools enable you to configure connections for several users using less than a one-to-one ratio of connections to users, because the connections in the pool are reusable. For example, a two-tier application requires one JDBC connection for its one user. A three-tier application, conversely, can support several thousand users with a connection pool of only a few connections, depending on the application. The connection pool assigns connections to clients, retrieves the connections when clients complete their tasks, and reuses them for future database requests. The connection pool also queues database requests when requests outnumber the available connections.

OracleAS TopLink supports two types of connection pools: the default OracleAS TopLink internal connection pool and external connection pools.

Internal Pools

Because of the multiuser nature of a server session, OracleAS TopLink establishes a connection pool for all server sessions by default. The pool includes several database connections that can be configured, and OracleAS TopLink manages the pool automatically.

External Pools

OracleAS TopLink supports external connection pools. Applications that include an application server usually use an external connection pool, managed by a Java Transaction Architecture (JTA) device.

JTA

The JTA is a specification that enables your application to participate in a distributed transaction system. The system provides transaction management and connection pooling and enables your application to interact with multiple databases transparently.

OracleAS TopLink applications that use an application server often use JTA to manage database transactions.

Data Conversion

OracleAS TopLink applications store object attributes on a database. To enable this functionality, OracleAS TopLink must convert object attributes, which are Java types such as STRING and INTEGER, to database types, such as VARCHAR and NUMERIC. The OracleAS TopLink conversion manager manages these conversions and enables you to build custom conversion classes.

Database Platforms

OracleAS TopLink communicates with databases using Structured Query Language (SQL). Because each database platform uses its own variation on the basic SQL language, OracleAS TopLink must adjust the SQL it uses to communicate with the database to ensure that the application runs smoothly.

This section describes OracleAS TopLink support for:

JDBC-SQL and Native SQL

By default, OracleAS TopLink accesses the database using JDBC-SQL and automatically performs the conversions between Java types and database types. OracleAS TopLink provides the conversions listed in Table 5-1 automatically.

Table 5-1 JDBC-SQL Conversion Types  
Class Oracle Type DB2 Type dBase Type Sybase Type Microsoft Access Type

java.lang.Boolean

NUMBER

SMALLINT

NUMBER

BIT default 0

SHORT

java.lang.Byte

NUMBER

SMALLINT

NUMBER

SMALLINT

SHORT

java.lang.Byte[]

LONG RAW

BLOB

BINARY

IMAGE

LONGBINARY

java.lang.Integer

NUMBER

INTEGER

NUMBER

INTEGER

LONG

java.lang.Long

NUMBER

INTEGER

NUMBER

NUMERIC

DOUBLE

java.lang.Float

NUMBER

FLOAT

NUMBER

FLOAT(16)

DOUBLE

java.lang.Double

NUMBER

FLOAT

NUMBER

FLOAT(32)

DOUBLE

java.lang.Short

NUMBER

SMALLINT

NUMBER

SMALLINT

SHORT

java.lang.String

VARCHAR2

VARCHAR

CHAR

VARCHAR

TEXT

java.lang.Character

CHAR

CHAR

CHAR

CHAR

TEXT

java.lang.Character[]

LONG

CLOB

MEMO

TEXT

LONGTEXT

java.math.BigDecimal

NUMBER

DECIMAL

NUMBER

NUMERIC

DOUBLE

java.math.BigInteger

NUMBER

DECIMAL

NUMBER

NUMERIC

DOUBLE

java.sql.Date

DATE

DATE

DATE

DATETIME

DATETIME

java.sql.Time

DATE

TIME

CHAR

DATETIME

DATETIME

java.sql.Timestamp

DATE

TIMESTAMP

CHAR

DATETIME

DATETIME

OracleAS TopLink provides the required customization by enabling you to specify your database platform. OracleAS TopLink provides specific support for the following platforms:

Specify your database platform in the login element of the sessions.xml file or the login section of your Java project configuration file (project.java). If you set your database platform in the OracleAS TopLink Mapping Workbench, then the OracleAS TopLink Mapping Workbench and the OracleAS TopLink Sessions Editor manage the database platform configuration for you automatically.

Example 5-1 Specifying an Oracle Database Platform in the sessions.xml File

For clarity, the code that sets the platform class is bold in this example.

<session>
  ...
  <login>
    ...
    <platform-class>oracle.toplink.internal.databaseaccess.OraclePlatform</platform-class>
    ...
  </login>
...
</session>

Example 5-2 Specifying an Oracle Database Platform in Java

project.getLogin().useOracle();

Custom Platforms

You can specify a custom database platform for your OracleAS TopLink application. Custom platform support enables you to use a database when OracleAS TopLink has no predefined platform.

To enable custom database platform support, create a new platform class that extends one of the existing platform classes, and call the class at runtime by referencing it in the session configuration file (the sessions.xml file), as Example 5-1 illustrates.

JDBC Connection Pools

A JDBC connection pool is a collection of reusable database connections that service a single application. This section introduces the following topics and techniques for working with JDBC connection pools:

Default Connection Pools

OracleAS TopLink provides a default internal connection pool for sessions that use a server session for database access. The default settings are appropriate for most applications; however, you can modify the connection pool attributes in the sessions.xml file to tailor the pool to your needs. You can specify:

For complete information on specifying the internal connection pool in the sessions.xml file, see "connection-pool Element".

External Connection Pools

With OracleAS TopLink you can use an external connection pool rather than the default internal pool, enabling you to leverage external transaction management systems such as JTA. This is common in applications that incorporate an application server.

To use an external connection pool, enable and specify it as follows:

JDBC Datasources

OracleAS TopLink uses a datasource to access your database information--your application does not need to be aware or maintain the connection information. OracleAS TopLink can access the database through a connection pool or a datasource. OracleAS TopLink JTA integration often uses a datasource.

You can configure a datasource as follows:

For more information about defined connection pools and datasources with EJB entity beans, see "Configuring the toplink-ejb-jar.xml File with the BEA WebLogic Server".

Container-Managed Persistence and Datasources

OracleAS TopLink CMP applications can leverage datasources rather than connection pools. To use a datasource, configure Java Transaction Service (JTS) support. JTS is the specification that supports JTA.

To use a datasource, configure both a JTS and a non-JTS datasource in the toplink-ejb-jar.xml file. To configure the required sources, specify them in the datasource and non-jts-data-source tags in the login element. These tags correspond to JTS and non-JTS datasources respectively.

The values for these datasource tags correspond directly to the names of the datasources as defined in your J2EE container or application server. Following is an example of a partial toplink-ejb-jar.xml file listing, using datasources:

...
<datasource>myJtsDataSource</datasource>
<non-jts-data-source>myNonJtsDataSource</non-jts-data-source>
...

For more information, see "Configuring the toplink-ejb-jar.xml File with the BEA WebLogic Server".

JTA

You can integrate your OracleAS TopLink application with a transaction service that complies with JTA, thereby enabling sessions to:

JTA is a Java 2 Enterprise Edition (J2EE) component.

For more information about leveraging JTA in your application, see "J2EE Integration".

Database Login Information

Java applications that access a database log in to the database through a JDBC driver. Database logins generally require a valid user name and password. OracleAS TopLink applications store this login information in the DatabaseLogin class. All sessions must have a valid DatabaseLogin instance before logging in to the database.

This section describes:

Creating a Login Object

Your project configuration file (project.xml or project.java) must include a login object to enable database access. The most basic login mechanism creates an instance of DatabaseLogin through its default constructor, as follows:

Databaselogin login = new Databaselogin();
...

If you create the project in the OracleAS TopLink Mapping Workbench, OracleAS TopLink creates the login object for you automatically and enables you to access the login from your project instance. This ensures that the session uses login information set in the OracleAS TopLink Mapping Workbench (for example: sequencing information) and also prevents you from inadvertently overwriting the login information already included in the project.

You can also access the login in Java code, using the getLogin() instance method to return the project's login. This method returns an instance of DatabaseLogin, which you can either use directly or augment with additional information before logging in.

Specifying Driver Information

The DatabaseLogin class includes helper methods that set the driver class, driver Uniform Resource Locator (URL) prefix, and database information for common drivers. When you use helper methods, use the setDatabaseURL() method to set the database instance for the JDBC driver URL.

These helper methods also specify any additional settings required for that driver, such as binding byte arrays or using native SQL.

For example:

project.getLogin().useOracleThinJDBCDriver();
project.getLogin().setDatabaseURL("dbserver:1521:orcl");

Using the Sun Microsystems JDBC-ODBC Bridge

To use the Sun Microsystems JDBC-ODBC bridge, specify the ODBC datasource name by calling the setDataSourceName().

Example 5-3 Using the Sun Microsystems JDBC-ODBC Bridge

project.getLogin().useJDBCODBCBridge();
project.getLogin().useOracle();
project.getLogin().setDataSourceName("Oracle");

In Example 5-3, OracleAS TopLink splits the URL into the driver and database calls. You can also use the setConnectionString() function to specify the URL in a single line of code.

Using a Different Driver

If you require a driver other than the Sun Microsystems JDBC-ODBC bridge, specify a different connection mechanism by calling the setDriverClass() and setConnectionString() methods.

For more information about the correct driver settings to use with these methods, see the driver documentation.

Example 5-4 Using an Alternative Driver

project.getLogin().setDriverClass(oracle.jdbc.driver.OracleDriver.class);
project.getLogin().setConnectionString("jdbc:oracle:thin:@dbserver:1521:orcl");

Setting Login Parameters

You can set several session properties as part of the login, including user information, database information, and JDBC driver information.

User Information

If a database requires user and password information, call the setUserName() and setPassword() methods after you specify the driver. Specify user and password information when you use the login object from a OracleAS TopLink Mapping Workbench project.

Example 5-5 Using setUserName() and setPassword()

project.getLogin().setUserName("userid");
project.getLogin().setPassword("password");

Database Information

You can specify properties such as the database name and the server name using the setServerName() and setDatabaseName() methods. The ODBC datasource Administrator for most JDBC-ODBC bridges usually sets these properties, but some drivers do require you to specify them explicitly.

Note that, because the database and server name properties are part of the database URL, most JDBC drivers do not require you to specify them explicitly and may fail if you do specify them.

Additional JDBC Properties

If your JDBC driver requires additional properties, use the setProperty() method to send these properties. Use caution when specifying properties, because, although some drivers require additional information, other drivers can fail if you specify properties that are not required. If you use the setProperty() method and the connection always fails, ensure that the specified properties are correct, complete, and required.


Note:

Do not set the login password directly using the setProperty() method, because OracleAS TopLink encrypts and decrypts the password. Use the setPassword() method instead.


Database Login Advanced Features

You can set the following options within your code, rather than through the OracleAS TopLink Mapping Workbench:

There are several options you can set at login rather than through more conventional methods, such as through the OracleAS TopLink Mapping Workbench.

Setting Sequencing at Login

For most projects, you set sequencing in the OracleAS TopLink Mapping Workbench project. To configure sequencing in Java code, you can use any of the following methods:

OracleAS TopLink supports native sequencing on Oracle databases, IBM Informix, Microsoft SQL Server, and Sybase SQL Server. Using native sequencing requires that you specify the database platform. Call the useNativeSequencing() method to configure your application to use native sequencing rather than a sequence table.

When you implement native sequencing, consider the following:

Example 5-6 Configuring Oracle Native Sequencing in Java Code

project.getLogin().useOracle();
project.getLogin().useNativeSequencing();
project.getLogin().setSequencePreallocationSize(1);


Note:

Using the Project class to create a DatabaseLogin instance automatically uses the sequencing information specified in the OracleAS TopLink Mapping Workbench.


For more information, see "Sequencing".

Setting Direct Connect Drivers

By default, OracleAS TopLink loads a JDBC driver and connects to a database as follows:

Some drivers do not allow you to use the java.sql.DriverManager to connect to a database. To load these drivers, configure OracleAS TopLink to instantiate the drivers directly, by invoking the DirectDriverConnect() method.

Example 5-7 Using useDirectDriverConnect()

project.getLogin().useDirectDriverConnect("com.direct.connectionDriver",
  "jdbc:far:", "server");

Using JDBC 2.0 Datasources

The JDBC 2.0 specification recommends using a Java Naming and Directory Interface (JNDI) naming service to acquire a connection to a database. To use this feature, configure an instance of oracle.toplink.jndi.JNDIConnector and pass it to the project login object using the setConnector() method.

Example 5-8 Using JNDI

import oracle.toplink.sessions.*;
import oracle.toplink.jndi.*;
    javax.naming.Context context = new javax.naming.InitialContext();
    Connector connector = new JNDIConnector(context, "customerDB");
    project.getLogin().setConnector(connector);

Using Custom Database Connections

OracleAS TopLink allows you to develop your own class to obtain a connection to a database. The class must implement the oracle.toplink.sessions.Connector interface. This requires the class to implement the following methods:

Implement the custom class, instantiate it, and then pass it to the project login object, using the setConnector() method.

Example 5-9 Using the oracle.toplink.sessions.Connector Interface

import oracle.toplink.sessions.*;
Connector connector = new MyConnector();
project.getLogin().setConnector(connector);

OracleAS TopLink Conversion Manager

OracleAS TopLink uses a class known as the ConversionManager to convert database types to Java types. This class, found in the oracle.toplink.internal.helper package, is the central location for type conversion and provides developers with a mechanism for using custom types in OracleAS TopLink.

This section describes:

Creating Custom Types with the Conversion Manager

Use the conversion manager to create and use custom types in OracleAS TopLink.

To use custom types in OracleAS TopLink:
  1. Use one of the following methods to create a subclass of the ConversionManager:

    • Overload the public Object convertObject(Object sourceObject, Class javaClass) method to call the conversion method you provide in the subclass for the custom type.

    • Delegate the conversion to the superclass.

  2. Implement the protected ClassX convertObjectToClassX(Object sourceObject) throws ConversionException conversion method to convert incoming objects to the required class.

  3. Assign the class to OracleAS TopLink in either of two ways:

    • Assign a custom conversion manager to the OracleAS TopLink session using the (getSession().getPlatform().setConversionManager (ConversionManager)) platform.

    • Set the conversion manager singleton by calling the setDefaultManager(ConversionManager) static method on the conversion manager. This setting causes all OracleAS TopLink sessions created in the Java virtual machine (JVM) to use the custom conversion manager. See the ConversionManager class in the Oracle Application Server TopLink API Reference for examples.

Conversion Manager Class Loader

OracleAS TopLink provides a class loader within the conversion manager which enables the conversion manager to load classes from both a OracleAS TopLink Mapping Workbench project and the class library. The conversion manager uses the System class loader by default.

Resolving Class Loader Exceptions

In some cases, such as when OracleAS TopLink is deployed within an application server, you may want to use other class loaders for the deployed classes. Doing this can cause a ClassNotFound exception. To resolve this problem, use one of the following methods:

Performance

You can use several techniques to improve data access performance for your application. This section discusses some of the more common approaches, including:

Data Optimization

By default, OracleAS TopLink optimizes data access by accessing the data from JDBC in the format the application requires. For example, OracleAS TopLink retrieves longs from JDBC instead of having the driver return a BigDecimal that OracleAS TopLink would then have to convert into a long.

OracleAS TopLink also retrieves dates as strings and converts directly to the date or Calendar type used by the application. Some older drivers do not convert data correctly. For example, earlier BEA WebLogic JDBC drivers cannot convert dates to strings in the correct format. If you use one of these drivers, disable data optimization.


Note:

The problems mentioned here may have been fixed in more recent versions of the drivers. See your vendor documentation for relevant updates.


Example 5-10 Disabling Data Optimization in Code

session.getLogin().dontOptimizeDataConversion() ;

Example 5-11 Disabling Data Optimization in the sessions.xml File

<login>
    ...
    <should_optimize_data_conversion>false</should-optimize-data-conversion>
</login>

Batch Writing

Batch writing can improve database performance by sending groups of INSERT, UPDATE, and DELETE statements to the database in a single transaction, rather than individually. OracleAS TopLink supports batch writing for selected databases and for JDBC 2.0 batch-compliant drivers.

To enable JDBC 2.0 batch writing, invoke the useBatchWriting() method on the login.

If you use a JDBC driver that does not support batch writing directly, you can still take advantage of batch writing, because OracleAS TopLink provides its own batch writing functionality. To enable OracleAS TopLink batch writing support, run the code in Example 5-12.

Example 5-12 Batch Writing

project.getLogin().useBatchWriting();
project.getLogin().dontUseJDBCBatchWriting();

For more information about batch writing, see Chapter 10, "Tuning for Performance".

Binding and Parameterized SQL

By default, OracleAS TopLink prints data inlined into its generated SQL and does not use parameterized SQL. However, you can implement parameterized SQL to:

OracleAS TopLink does not implement parameterized SQL because many JDBC drivers do not fully support parameter binding, and have size or type limits.

For more information about binding and binding size limits, see your database documentation.

If your driver supports parameter binding and also imposes a limit on the size of the printable results, use parameter binding to accommodate large binary data in one of the following ways:

Example 5-13 Using Parameter Binding with Large Binary Data

project.getLogin().useByteArrayBinding();

project.getLogin().useStreamsForBinding();
project.getLogin().useStringBinding(50);
project.getLogin().bindAllParameters();
project.getLogin().cacheAllStatements();
project.getLogin().setStatementCacheSize(50);

Prepared Statement Caching

OracleAS TopLink enables you to cache JDBC prepared statements to improve query performance. Prepared statements improve database performance by reducing the number of times the database SQL engine parses and prepares a SQL call for a frequently called query.

To enable prepared statement caching, cache the statement and bind its parameters. You can do this at the query level or at the session level.

Prepared Statement Caching for a Query

To cache the prepared statement for an individual query, configure statement caching in the query definition before execution the query. You can do this either in Java code, or as part of the SQL for a named query in the OracleAS TopLink Mapping Workbench.

Example 5-14 Caching a Prepared Statement in Code for an Individual Query

// Add a query.
ExpressionBuilder builder = new ExpressionBuilder();
ReadAllQuery query = new ReadAllQuery(PhoneNumber.class, builder);

Expression exp = builder.get("id").equal(builder.getParameter("ID"));	
query.setSelectionCriteria(exp.and(builder.get("areaCode").equal("613")));

query.addArgument("ID");

/* The following options force OracleAS TopLink to cache the prepared statement 
and bind any arguments required by the query */
query.cacheStatement();
query.bindAllParameters();

descriptor.getQueryManager().addQuery("localNumbers", query);

Prepared Statement Caching for a Session

To cache all prepared statements for a session, edit the sessions.xml file in the OracleAS TopLink Sessions Editor, adding login options to bind all parameters and cache statements.

Example 5-15 Caching Prepared Statements in the sessions.xml File

<session>
    ...
    <login>
        ...
        <should-bind-all-parameters>true</should-bind-all-parameters>
        <should-cache-all-statements>true</should-cache-all-statements>
    </login>
    ....
</session>

Table Qualifier

A table qualifier affects the data in a table to which a user has access. You can use table qualifiers to manage data access in databases that support them, such as Oracle and IBM DB2. You can also use table qualifiers to fully-qualify the table names of tables that have a different creator.

OracleAS TopLink enables you to add a table qualifier to all table references in a given session. Use the setTableQualifier() method on your session login object to prepend a string to all tables accessed by the session.

Example 5-16 Adding a Table Qualifier

session.getLogin().setTableQualifier([QUALIFIER_STRING])

Locking Policy

A locking policy is an important component of any multi-user OracleAS TopLink application. When users share objects in an application, a locking policy ensures that two or more users do not attempt to modify the same object or its underlying data simultaneously.

OracleAS TopLink works with relational databases to provide support for several types of locking policy, including:

Optimistic Lock

All users have read access to the object. When a user attempts to write a change, the application checks to ensure the object has not changed since the last read. OracleAS TopLink provides this locking policy.

Optimistic Read Lock

As with optimistic lock, the optimistic read lock ensures that the object has not changed before writing a change. However, the optimistic read lock also forces a read of any related tables that contribute information to the object. OracleAS TopLink offers this locking policy.

Pessimistic Locking

When a user accesses an object to update it, the database locks the object until the update is completed. No other user can read or update the object until the first user releases the lock. The database offers this locking type.

No Locking

The application does not verify that data is current.


Note:

Most OracleAS TopLink applications use either optimistic locking or optimistic read locking, because they are the safest and most efficient of these locking strategies.


Using Optimistic Locking

Optimistic locking, also known as write locking, allows unlimited read access to a given object, but allows a client to modify the object only if the object has not changed since the client last read it.

Optimistic locking checks an object's version at transaction commit time against the version read during the transaction. This check ensures that no other client modified the data after it was read by the current transaction. If this check detects stale data, the check raises an OptimisticLockException, and the commit fails.


Note:

Using optimistic locking by itself does not protect against having different copies of the same object existing in multiple nodes. For more information, see "Optimistic Locking" in the Oracle Application Server TopLink Mapping Workbench User's Guide.


Set optimistic locking on the descriptor using one of two locking policies:

For more information about locking policies, see "Two Different Locking Policies".

Advantages and Disadvantages of Optimistic Locking

Here are the advantages of optimistic locking:

However, optimistic locking cannot prevent applications from selecting and attempting to modify the same data. When two different processes modify data, the first one to commit the changes succeeds while the other process fails and receives an OptimisticLockException.

Advanced Optimistic Locking Policies

All OracleAS TopLink optimistic locking policies implement the OptimisticLockingPolicy interface. This interface includes several of methods that you can implement to customize the optimistic locking policy.

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

Optimistic Read Locking

Optimistic read lock is an advanced type of optimistic lock that enables you to force lock checking on objects that are not modified by the current transaction. Optimistic read lock also offers the option to increment the unchanged object version or leave the version unchanged.

For example, consider a transaction that updates a mortgage rate by multiplying the central bank prime rate by 1.25. The transaction executes an optimistic read lock on the central prime rate at commit time to ensure that the prime rate has not changed since the transaction began. Note that in this example, the transaction does not increment the version of the unchanged object (the central prime rate).

Example 5-17 Optimistic Read Lock with No Version Increment

try {
    UnitOfWork uow = session.acquireUnitOfWork();
    MortgageRate cloneMortgageRate = (MortgageRate)
      uow.registerObject(mortgageRate);
    CentralPrimeRate cloneCentralPrimeRate = (CentralPrimeRate)
      uow.registerObject(CentralPrimeRate);
    /* Change the Mortgage Rate */
    cloneMortgageRate.setRate(cloneCentralPrimeRate.getRate() * 1.25);
    /* Optimistic read lock check on Central prime rate with no version update*/
    uow.forceUpdateToVersionField(cloneCentralPrimeRate, false);
    uow.commit();
}(OptimisticLockException exception) {
    /* Refresh the out-of-date object */
    session.refreshObject(exception.getObject());
    /* Retry... */
}

Consider another example, in which an invoice thread calculates an invoice for a customer. If another thread (the service thread) adds a service to the same customer or modifies the current service, it must inform the invoice thread, which adds the changes to the invoice. This feature is available for objects that implement a version of field locking policy or timestamp locking policy. When you update an object that implements a version locking policy, the version value is incremented or set to the current timestamp.

For more information about field locking policies, see "Field Locking Policies".

Example 5-18 Optimistic Read Lock with Version Increment

/* The following code represents the service thread. Notice that the thread 
forces a version update. */
try {
    UnitOfWork uow = session.acquireUnitOfWork();
    Customer cloneCustomer = (Customer uow.registerObject(customer);
    Service cloneService = (Service uow.registerObject(service);
    /* Add a service to customer */
    cloneService.setCustomer(cloneCustomer);
    cloneCustomer.getServices().add(cloneService);
    /* Modify the customer version to inform other application that 
      the customer has changed */
    uow.forceUpdateToVersionField(cloneCustomer, true);
    uow.commit();
}
catch {
    (OptimisticLockException exception) {
    /* Refresh out-of-date object */
    session.refreshObject(exception.getObject());
    /* Retry... */
}

/* The following code represents the invoice thread, and calculates a bill for 
the customer. Notice that it does not force an update to the version */

try {
    UnitOfWork uow = session.acquireUnitOfWork();
    Customer cloneCustomer = (Customer) uow.registerObject(customer);
    Invoice cloneInvoice = (Invoice) uow.registerObject(new Invoice());
    cloneInvoice.setCustomer(cloneCustomer);
    /* Calculate services' charge */
    int total = 0;
    for(Enumeration enum = cloneCustomer.getServices().elements(); 
    enum.hasMoreElements();) {
    total += ((Service) enum.nextElement()).getCost();
    }
    cloneInvoice.setTotal(total);
    /* Force optimistic lock checking on the customer to guarantee a valid 
      calculation */
    uow.forceUpdateToVersionField(cloneCustomer, false);
    uow.commit();
}
catch {
    (OptimisticLockException exception) {
    /* Refresh the customer and its privately owned parts */
    session.refreshObject(cloneCustomer);
    /* If the customer's services are not private owned then use a 
    ReadObjectQuery to refresh all parts */
    ReadObjectQuery query = new ReadObjectQuery(customer);
    /* Refresh the cache with the query's result and cascade refreshing 
      to all parts including customer's services */
    query.refreshIdentityMapResult();
    query.cascadeAllParts();
    /* Refresh from the database */
    query.dontCheckCache();
    session.executeQuery(query);
    /* Retry... */
}

When is an Object Considered Changed?

The Unit of Work considers an object changed when you modify its direct-to-field or aggregate object mapping attribute. Adding, removing, or modifying objects related to the source object does not render the source object changed for the purposes of the Unit of Work.

Pessimistic Locking

Pessimistic locking locks objects when the transaction accesses them, before commit time, ensuring that only one client is editing the object at any given time.

Pessimistic locking detects locking violations at object read time. The OracleAS TopLink implementation of pessimistic locking uses database row-level locks, such that attempts to read a locked row either fail or are blocked until the row is unlocked, depending on the database.

Example 5-19 Pessimistic Locking with ReadObjectQuery

import oracle.toplink.sessions.*;
import oracle.toplink.queryframework.*;

...
UnitOfWork uow = session.acquireUnitOfWork();

ReadObjectQuery query = new ReadObjectQuery();
query.setReferenceClass(Employee.class);
query.acquireLocks(); 
Employee employee = (Employee) uow.executeQuery(query);

// Make changes to object
...

uow.commit();
...

Example 5-20 Pessimistic Locking with ReadAllQuery

import oracle.toplink.sessions.*;
import oracle.toplink.queryframework.*;

...
UnitOfWork uow = session.acquireUnitOfWork();
ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(Employee.class);
query.setSelectionCriteria(new ExpressionBuilder().get("salary").greaterThan(25000));
query.acquireLocks(); 
/* NOTE: the objects are registered when they are obtained by using Unit of Work. OracleAS 
TopLink will update all the changes to registered objects when Unit of Work commit */
Vector employees = (Vector) uow.executeQuery(query);
    // Make changes to objects
    ...
    uow.commit();
...

Example 5-21 Pessimistic Locking with a Session Using ReadAllQuery

import oracle.toplink.sessions.*;
import oracle.toplink.sessions.queryframework.*;
...
// It must begin a transaction or the lock request will throw an exception
session.beginTransaction();
ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(Employee.class);
query.setSelectionCriteria(new ExpressionBuilder().get("salary").greaterThan(25000));
query.acquireLocks(); 
// or acquireLocksWithoutWaiting()
query.refreshIdentityMapResult();
Vector employees = (Vector) session.executeQuery(query);
// Make changes to objects
...
// Update objects to reflect changes
for (Enumeration enum = employees.elements(); 
    employees.hasMoreElements(); {
    session.updateObject(enum.nextElement());
}
session.commitTransaction();
...

Pessimistic Locking and the Cache

When you acquire a pessimistic lock on an object, you refresh the object in the session cache. This is different from an optimistic lock, which refreshes objects in the cache only after a successful commit. Because of this, and because it prevents other processes from reading locked objects, a pessimistic lock is not as efficient as an optimistic lock.


Note:

OracleAS TopLink uses database row-level locking to implement pessimistic locking. Although this is the standard way of implementing pessimistic locking in the database, not all databases support row-level locking functionality. Consult your database documentation to see if your database supports row-level locking and the SELECT ... FOR UPDATE [NO WAIT] API.


Pessimistic Locking and Database Transactions

Because pessimistic locks exist for the duration of the current transaction, the associated database transaction remains open from the point of the first lock request until the transaction commits. When the transaction commits or rolls back, the database releases the locks.

The Unit of Work starts a database transaction automatically when it attempts to read the first object in its operations. If you are not using the Unit of Work, manually begin a transaction on the session.

WAIT and NO_WAIT Options

OracleAS TopLink offers two methods of locking, WAIT and NO_WAIT. These options determine how the transaction responds when it encounters a locked row. If you select the:

Example 5-22 Pessimistic Locking with Wait for Lock

This example illustrates a pessimistic lock with the WAIT mode in the context of a Unit of Work.

import oracle.toplink.sessions.*;
import oracle.toplink.queryframework.*;
...
UnitOfWork uow = session.acquireUnitOfWork();
Employee employee = (Employee) uow.readObject(Employee.class);

/* Note: This will cause the Unit of Work to begin a transaction. In a 
three-Tier model this will also cause the ClientSession to acquire its write 
connection from the ServerSession's pool */
uow.refreshAndLockObject(employee, ObjectLevelReadQuery.LOCK);
// Make changes to object
...
uow.commit();
...

Example 5-23 Pessimistic Locking with No Wait for Lock

This example illustrates a pessimistic lock with the No_Wait mode in the context of a Unit of Work.

import oracle.toplink.sessions.*;
import oracle.toplink.queryframework.*;
import oracle.toplink.exceptions.*;
...
UnitOfWork uow = session.acquireUnitOfWork();
Employee employee = (Employee) uow.readObject(Employee.class);

try {
    employee = (Employee)
      uow.refreshAndLockObject(employee,ObjectLevelReadQuery.LOCK_NOWAIT);
} 
catch (DatabaseException dbe) {
    // Some databases throw an exception instead of returning nothing.
    employee = null;
}
if (employee == null) {
    // Lock cannot be obtained
    uow.release();
    throw new Exception("Locking error.");
} else {
    // Make changes to object
    ...
    uow.commit();
}
...

Advantages of Pessimistic Locking

The following are the advantages of pessimistic locking:

Disadvantages of Pessimistic Locking

The following are the disadvantages of pessimistic locking:

Reference

Table 5-2 summarizes the most common public methods for Pessimistic Locking. The Default column describes default settings of the descriptor element. For more information about the available methods for Pessimistic Locking, see the Oracle Application Server TopLink API Reference.

Table 5-2 Elements for Pessimistic Locking  
Element Default Method Name

Lock mode
(for ObjectLevelRead Query)

No lock

acquireLocks()
acquireLocksWithoutWaiting()

Refresh and lock
(for Session)

not applicable

refreshAndLockObject(Object object, short 
lockMode)

Two Different Locking Policies

A locking policy describes how you manage record locking on the database and track changed objects. OracleAS TopLink offers two different strategies for managing locking: field locking and timestamp locking.

Field Locking Policies

Field locking policies compare the current values of certain mapped fields with previous values. OracleAS TopLink support for field locking policies does not require any additional fields in the database. Field locking policy support includes:

These policies require you to use a Unit of Work for database updates. Each policy handles its field comparisons in a specific way defined by the policy:

When an update fails due to an optimistic locking violation, OracleAS TopLink raises an OptimisticLockException. Under most circumstances, the application handles this exception by refreshing the object and reapplying changes.

Version Locking Policies

OracleAS TopLink supports version locking policies through the VersionLockingPolicy interface and the TimestampLockingPolicy interface. Each of these policies requires an additional field in the database to operate:

OracleAS TopLink records the version as it reads an object from a table. When the client attempts to write the object, OracleAS TopLink compares the object version with the version in the table record. If the versions match, OracleAS TopLink writes the updated object to the table and updates the version of both the table record and the object. If the versions are different, the write fails and OracleAS TopLink raises an error.

These two version locking policies have different ways of writing the version fields back to the database:

For either policy, you write the value of the write lock field in either the identity map or in a writable mapping within the object.

If you store the value in the identity map, you do not require an attribute mapping for the version field. However, if the application does map the field, the mappings must be read-only to allow OracleAS TopLink to control writing the fields.

Timestamp Versus Version Locking Policies

When choosing a locking policy, consider the following:

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

In Step 3, OracleAS TopLink uses an internal mechanism to generate the calls, based on your chosen data repository. The SDK enables you replace the internal mechanism with one of your own design. This enables you to develop custom calls that address non-relational 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:

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 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 accessor's connection 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's 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:

If you use OracleAS TopLink relationship mappings, implement the appropriate calls to read the reference object(s) 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. This allows 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 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 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, generally 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, generally 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, generally 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:

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 provides 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's query manager.

Example 5-26 Building a Database Query for the Descriptor's 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:

Unsupported properties

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

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:

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 provides 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 object's DescriptorQueryManager.

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's 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

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

Structure Mapping

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

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

Reference Mapping

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

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

Array Mapping

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

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

Object Array Mapping

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

Nested Table Mapping

Because the nested table mapping depends on the object-relational data model, the OracleAS TopLink SDK does not support it. 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:

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. As a result, 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 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 an element object's primary key.

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 mapping's custom selection query uses to read in the elements of the object collection. The field names in the nested database rows must match those expected by the custom selection query's call.

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:

SDK Platform and Sequencing

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

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:

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:

OracleAS TopLink XML Support

OracleAS TopLink enables you to read and modify objects in XML files. Object-to-XML (O-X) mapping enables your application to deal exclusively with objects, rather than managing the intricacies of XML parsing and deconstruction. You can use OracleAS TopLink XML support to exchange data with other applications (for example: legacy applications or business partner applications).

This section describes:

The OracleAS TopLink implementation of XML support uses a file and directory paradigm to store information, as follows:

Getting Started

The default XML extension is similar to a regular OracleAS TopLink project. Use the following steps to develop your application:

  1. Configure your login using an XMLFileLogin.

    XMLFileLogin login = new XMLFileLogin();
    login.setBaseDirectoryName("C:\Employee Database");
    
    // set up the sequences
    login.setSequenceRootElementName("sequence");
    login.setSequenceNameElementName("name");
    login.setSequenceCounterElementName("count");
    
    // create the directories if they don't already exist
    login.createDirectoriesAsNeeded();
    
    
  2. Build your project.

    Project project = new Project(login);
    project.addDescriptor(buildEmployeeDescriptor());
    project.addDescriptor(buildAddressDescriptor());
    project.addDescriptor(buildProjectDescriptor());
    // etc.
    
    
  3. Build your descriptors using XMLDescriptors.

    XMLDescriptor descriptor = new XMLDescriptor();
    descriptor.setJavaClass(Employee.class);
    descriptor.setRootElementName("employee");
    descriptor.setPrimaryKeyElementName("id");
    descriptor.setSequenceNumberName("employee");
    descriptor.setSequenceNumberElementName("id");
    // etc.
    
    
  4. Build your mappings. For the XML extension, OneToOneMappings and SDKObjectCollectionMappings require custom selection queries as follows:

    // 1:1 mapping
    OneToOneMapping addressMapping = new OneToOneMapping();
    addressMapping.setAttributeName("address");
    addressMapping.setReferenceClass(Address.class);
    addressMapping.privateOwnedRelationship();
    addressMapping.setForeignKeyFieldName("addressId");
    // build the custom selection query
    ReadObjectQuery addressQuery = new ReadObjectQuery();
    addressQuery.setCall(new XMLReadCall(addressMapping));
    addressMapping.setCustomSelectionQuery(addressQuery);
    descriptor.addMapping(addressMapping);
    // 1:n mapping
    
    SDKObjectCollectionMapping projectsMapping = new 
      SDKObjectCollectionMapping();
    projectsMapping.setAttributeName("projects");
    projectsMapping.setReferenceClass(Project.class);
    projectsMapping.setFieldName("projects");
    projectsMapping.setSourceForeignKeyFieldName("projectId");
    projectsMapping.setReferenceDataTypeName("project");
    // use convenience method to build the custom selection query
    projectsMapping.setSelectionCall(new XMLReadAllCall(projectsMapping));
    descriptor.addMapping(projectsMapping);
    
    
  5. Build your database session and log in.

    DatabaseSession session = project.createDatabaseSession();session.login();
    
    
  6. Configure sequencing, if necessary.

    (new XMLSchemaManager(session)).createSequences();
    
    
  7. Run your application normally.

    For example:

    Vector employees = session.readAllObjects(Employee.class);
    Employee employee = (Employee) employees.firstElement();
    UnitOfWork uow = session.acquireUnitOfWork();
    Employee employeeClone = uow.registerObject(employee);
    employeeClone.setSalary(employeeClone.getSalary() + 50);
    uow.commit();
    
    
  8. Log out when your session is complete.

    session.logout();
    
    

Customizations

You can customize the OracleAS TopLink XML extension in two key ways, by modifying:

Implementation Details

The package oracle.toplink.xml contains the classes that implement OracleAS TopLink support for O-X mapping. These classes represent a simple example of how to use the OracleAS TopLink SDK as "Using the OracleAS TopLink SDK" describes.

The XML package defines its own set of interfaces, in addition to the SDK interfaces. You can use these interfaces to alter how you map your objects to XML documents, without re-implementing the entire SDK suite of interfaces and subclasses.

The XML extension includes the following implementations of the SDK interfaces and subclasses:

The XML extension also defines its own set of interfaces into which you can plug your own implementation classes, as follows:

These interfaces enable you to easily alter the way your objects map to XML documents.

XML File Accessor

The XMLFileAccessor is a subclass of the SDKAccessor that defines how the application stores XML documents in a native file system. As a subclass of SDK accessor, the XML file accessor does not have to implement any of the accessor protocol, although it does implement the connect(DatabaseLogin, Session) method.

The XML file accessor uses the standard SDK method of call execution and does not support transaction processing. This limitation is typical of native file systems.

XML Accessor Implementation

In addition to the Accessor interface, the XML file accessor implements the XMLAccessor interface. The XMLAccessor interface defines the protocol necessary to fetch streams of data for reading and writing XML documents. The XML file accessor implements this protocol by wrapping files in streams that can be used by the XML calls to read or write XML documents.

The XMLAccessor methods defined to fetch a stream (either a java.io.Reader or java.ioWriter) generally require three parameters:

The XML file accessor resolves the values of these three parameters to a File. It wraps the file in a stream (either a java.io.FileReader or a java.io.FileWriter) and returns it to the XML call for processing.

The XML file accessor calculates the file name as follows:

Directory Creation

You can configure the XML file accessor to create directories automatically when required. To enable this, include the createsDirectoriesAsNeeded call, set to TRUE, in the XML file login.

The createsDirectoriesAsNeeded call causes the accessor to create directories as required, including the base directory. If you set this call to FALSE, the accessor throws an XML data store exception if it encounters a request for an XML document that resolves to a nonexistent directory.

The default for this setting is FALSE. Set it to TRUE to enable directory creation.

XML Call

The XML call and its subclasses are the layer between the OracleAS TopLink database queries call interface and the XML document accessing protocol provided by an XML accessor.

XML calls include two properties:

XML Stream Policy

The XMLStreamPolicy is an interface that defines a protocol to fetch streams of data for reading and writing XML documents. XML calls use the implementation from the XML accessor stream policy. This implementation delegates every request for a stream to the XML accessor.

This policy enables you to override the default behavior on a per-call basis. For example, you can name a specific file in a call, rather than relying on the XML file accessor to resolve the required file name. XML file stream policy provides this behavior. To access it use the methods XMLCall.setFile(File) and XMLCall.setFileName(String).

XML Translator

XMLCalls use the XMLTranslator object to translate data between an XML document and an OracleAS TopLink database row. This pluggable interface enables you to modify the behavior of the XML calls. The XML calls default implementation of XML translator is DefaultXMLtranslator.

XMLTranslator Implementations

Several subclasses of XML call provide concrete implementations of call and SDK call. These classes differ in their implementations of the Call.execute(DatabaseRow, Accessor) method.

XML translator implementations offer object calls and data calls.

Object-Level Calls

Object-level calls enable you to call for objects from the datasource. All object calls other than Read calls require an association with a database query. OracleAS TopLink provides this automatically when you build a database query and configure it to use a custom call.

Read calls are an exception, because they are associated with a relationship mapping do not require an associated database query.

The following subclasses enable you to manipulate objects:

XML Read Call

If an XMLReadCall includes a reference to a one-to-one mapping, it extracts the foreign key for the mapping's relationship from the database row passed in to the execute(DatabaseRow, Accessor) method. If there is no mapping, the XML read call extracts the primary key for the query's associated descriptor from the database row.

In either case, XML read call then uses the resulting key to find the appropriate XML document.

XML Read All Call

If the XMLReadAllCall includes a reference to an SDK object collection mapping, it extracts the foreign keys for the mapping's relationship from the database row passed in to the execute(DatabaseRow, Accessor) method. It then uses the foreign keys to find the appropriate XML documents.

If no mapping is present, the XML read all call determines the root element name for the query's associated descriptor and returns all the DatasebaseRows for that root element name.

XML Insert Call

An XMLInsertCall takes the database row passed in to the execute(DatabaseRow, Accessor) method and uses the primary key to find the appropriate XML document stream. It then takes the modify row from the associated ModifyQuery, converts it to an XML document, and writes it out.

If the XML document exists, XML insert call raises an XML data store exception.

XML Update Call

An XMLUpdateCall takes the database row passed in to the execute(DatabaseRow, Accessor) method and uses the primary key to find the appropriate XML document stream. It then takes the modify row from the associated ModifyQuery, converts it to an XML document, and writes it out.

If the XML document does not exist, XML update call raises an XML data store exception.

XML Delete Call

An XMLDeleteCall takes the database row passed in to the execute (DatabaseRow, Accessor) method and uses the primary key to find the appropriate XML document stream. It then deletes this stream.

If the XML document exists, the call returns a row count of one. If not, the call returns a row count of zero.

XML Does Exist Call

An XML does exist call takes the database row passed in to the execute(DatabaseRow, Accessor) method and uses the primary key to find the appropriate XML document stream. If the document exists, OracleAS TopLink converts it to a database row to verify the object's existence. If the object does not exist, OracleAS TopLink returns a null.

Data Calls

Data calls enable you to retrieve data, rather than objects, from the datasource. Because XML data calls are not associated with a database query, they require a root element name and a set of ordered primary key element names. Pass these settings, along with the appropriate database row, to the XML stream policy at runtime. OracleAS TopLink uses this information to determine the appropriate XML document stream.

Example 5-47 A Typical Data Call

XMLDataReadCall call = new XMLDataReadCall();
call.setRootElementName("employee");
call.setPrimaryKeyElementName("id");

The following subclasses provide data call functionality:

XML Data Read Call

An XML data read call takes the database row passed in to the execute (DatabaseRow, Accessor) method and uses the primary key to find the appropriate XML document stream, and converts the stream to a database row. OracleAS TopLink returns the database row in a vector to ensure a consistent result object.

If the XML data read call does not include a primary key element, it performs a simple read-all for all the XML documents, with the specified root element name. XML data read call converts these and returns them as a vector of database rows.

You can further configure XML data read calls to specify the fields to return and their types.

Example 5-48 An XML Data Read Call

XMLDataReadCall call = new XMLDataReadCall();
call.setRootElementName("employee");
call.setPrimaryKeyElementName("id");
call.setResultElementName("salary");
call.setResultElementType(java.math.BigDecimal.class);

XML Data Insert Call

An XML data insert call takes the database row passed in to the execute(DatabaseRow, Accessor) method and uses the primary key to find the appropriate XML document stream. It then converts that row to an XML document, and writes it out.

If the XML document already exists, XML data insert call raises an XML data store exception.

XML Data Update Call

An XML data update call takes the database row passed in to the execute(DatabaseRow, Accessor) method and uses the primary key to find the appropriate XML document stream. It then converts that row to an XML document and writes it out.

If the XML document does not already exist, XML data update call raises an XML data store exception.

XML Data Delete Call

An XML data delete call takes the database row passed in to the execute(DatabaseRow, Accessor) method and uses the primary key to find the appropriate XML document stream. It then deletes this stream.

If the XML document already exists, the call returns a row count of one. If not, the call returns a row count of zero.

XML Descriptor

An XMLDescriptor is a subclass of the SDKDescriptor that:

XML Platform

XML platform is a subclass of SDK platform that implements the methods required to support sequence numbers: buildSelectSequenceCall() and buildUpdateSequenceCall(). These methods build and return the XML data calls that allow OracleAS TopLink to use sequence numbers maintained in XML documents.

To set the root element name for these XML documents, and the names of the elements used to hold the sequence name and sequence counter, specify these elements through the XML file login.

XML File Login

XML file login is a subclass of SDK login that allows you to configure the XML file accessor and XML platform. Use the XML file login to configure the following settings:

XML Schema Manager

XML schema manager is a subclass of SDK schema manager. It provides support for building the XML-based sequences required by your OracleAS TopLink database session. After you build your OracleAS TopLink project, use it to create a database session. Then you can log in and create the required sequences with the XML schema manager.

Example 5-49 Using XML Schema Manager for Sequencing

DatabaseSession session = project.createDatabaseSession();
session.login();
SchemaManager manager = new XMLSchemaManager(session);
manager.createSequences();

XML Accessor

XMLAccessor is an interface that extends the oracle.toplink.internal.databaseaccess.Accessor interface. It is the default interface that XML calls use to access streams for a given XML document.

To store XML documents in a non-native file system, provide a custom implementation of this interface. For example, to access your XML documents with a messaging service such the Java Message Service (JMS), you can develop an implementation of XML accessor that translates the method calls into JMS calls.

If you build a custom accessor, configure an XML login to use it.

Example 5-50 Using a Custom Accessor with XML Login

XMLLogin login = new XMLLogin();
login.setAccessorClass(XMLJMSAccessor.class);
login.setUserName("user");
login.setPassword("password");
// etc.

XML Translator

XML calls use the XMLTranslator interface to manipulate XML documents stored in a non-native file system. Each XML call has its own XML translator. The default XML translator is an instance of DefaultXMLTranslator, but you can replace the DefaultXMLTranslator with your own custom implementation.

XML translator defines the following protocol:

Default XML Translator

As the default XML translator for XML calls, DefaultXMLTranslator performs translations between database rows and XML documents. To enable translations with the default XML translator:

The DefaultXMLTranslator delegates the translation to two other classes:

XML Zip File Extension

The XML zip file extension is an enhancement to the XML implementation of the SDK. This extension adds the flexibility of maintaining the XML data store in archive files rather than in the directory/file structure of the standard XML data store. The format is similar to the standard XML data store; however, archive files replace directories in representing tables. The archive contains XML documents that map back to a database row in the same manner as if you stored them in a directory.

Using the Zip File Extension

To use the XML Zip file extension, configure your XML login to use the XML zip file accessor.

XMLLogin login = new XMLLogin();
login.setAccessorClass(XMLZipFileAccessor.class);

Configure Direct File Access With Zip File Extension

To access an XML document within an archive file, the call must know both the archive file location and the name of the XML document entry within the archive. Therefore, the setFileName() message sent to an XML call must include both the archive file and the XML document entry name, as follows:

XMLReadCall call = new XMLReadCall();
call.setFileName("C:/Employee DataStore/employee.zip", "1.xml");

Implementation Details

Zip file support requires the following two packages, stored in the package oracle.toplink.xml.zip:


Go to previous page Go to next page
Oracle
Copyright © 2000, 2003 Oracle Corporation.

All Rights Reserved.
Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index