Oracle Application Server TopLink Application Developer's Guide 10g (9.0.4) Part Number B10313-01 |
|
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:
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.
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.
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".
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.
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.
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.
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.
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.
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:
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.
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.
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>
project.getLogin().useOracle();
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.
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:
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".
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:
toplink-ejb-jar.xml
file, using the elements described in Table 9-1, "login Elements".
sessions.xml
file using the elements described in "connection-pool Element".
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:
toplink-ejb-jar.xml
file, using the elements described in Table 9-1, "login Elements".
sessions.xml
file login
element, using the optional data-source
element described in Table 4-3, "Basic Configuration Tags Within the Login Element".
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".
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".
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".
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:
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.
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");
To use the Sun Microsystems JDBC-ODBC bridge, specify the ODBC datasource name by calling the setDataSourceName()
.
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.
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.
project.getLogin().setDriverClass(oracle.jdbc.driver.OracleDriver.class); project.getLogin().setConnectionString("jdbc:oracle:thin:@dbserver:1521:orcl");
You can set several session properties as part of the login, including user information, database information, and JDBC driver 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.
project.getLogin().setUserName("userid"); project.getLogin().setPassword("password");
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.
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.
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.
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:
setSequenceCounterFieldName()
setSequenceNameFieldName()
setSequencePreallocationSize()
setSequenceTableName(
)
useNativeSequencing()
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:
project.getLogin().useOracle(); project.getLogin().useNativeSequencing(); project.getLogin().setSequencePreallocationSize(1);
For more information, see "Sequencing".
By default, OracleAS TopLink loads a JDBC driver and connects to a database as follows:
java.lang.Class.forName()
.
java.sql.DriverManager.getConnection()
.
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.
project.getLogin().useDirectDriverConnect("com.direct.connectionDriver", "jdbc:far:", "server");
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.
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);
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:
java.sql.Connection connect(java.util.Properties properties)
: Receives a dictionary of properties (including the user name and password) and must return a valid connection to the database.
void toString(PrintWriter writer)
: Prints out any helpful information on the OracleAS TopLink log.
Implement the custom class, instantiate it, and then pass it to the project login object, using the setConnector()
method.
import oracle.toplink.sessions.*; Connector connector = new MyConnector(); project.getLogin().setConnector(connector);
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:
Use the conversion manager to create and use custom types in OracleAS TopLink.
ConversionManager
:
protected ClassX convertObjectToClassX(Object sourceObject) throws ConversionException
conversion method to convert incoming objects to the required class.
(getSession().getPlatform().setConversionManager (ConversionManager))
platform.
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.
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.
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:
public void setShouldUseClassLoaderFromCurrentThread(boolean useCurrentThread)
method on the default conversion manager before logging in any sessions. This method resolves the problem for most application servers and ensures that OracleAS TopLink uses the correct class loader.
getSession()
call to set the required class loader on the conversion manager.
public static void setDefaultLoader(ClassLoader classLoader)
on the conversion manager before any sessions are logged in, and pass in the class loader that contains the deployed classes.
You can use several techniques to improve data access performance for your application. This section discusses some of the more common approaches, including:
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.
session.getLogin().dontOptimizeDataConversion() ;
<login> ... <should_optimize_data_conversion>false</should-optimize-data-conversion> </login>
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.
project.getLogin().useBatchWriting(); project.getLogin().dontUseJDBCBatchWriting();
For more information about batch writing, see Chapter 10, "Tuning for Performance".
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:
useByteArrayBinding()
method. This is a common method to accommodate large binary data.
useStreamsForBinding()
method.
useStringBinding()
method.
project.getLogin().useByteArrayBinding(); project.getLogin().useStreamsForBinding(); project.getLogin().useStringBinding(50); project.getLogin().bindAllParameters(); project.getLogin().cacheAllStatements(); project.getLogin().setStatementCacheSize(50);
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.
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.
// 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 ca
che the prepared statement
and bind any arguments required by the query */
query.cacheStatement();
query.bindAllParameters();
descriptor.getQueryManager().addQuery("localNumbers", query);
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.
<session> ... <login> ... <should-bind-all-parameters>true</should-bind-all-parameters> <should-cache-all-statements>true</should-cache-all-statements> </login> .... </session>
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.
session.getLogin().setTableQualifier([QUALIFIER_STRING])
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:
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.
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.
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.
The application does not verify that data is current.
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".
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
.
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 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).
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".
/* 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... */
}
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 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.
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();
...
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(); ...
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(); ...
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.
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.
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:
WAIT
option, then the transaction waits until the database releases the lock on the object. It then obtains a lock on the object and continues.
NO_WAIT
option, then OracleAS TopLink throws an exception when the transaction encounters a locked row.
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(); ...
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(); } ...
The following are the advantages of pessimistic locking:
Disadvantages of Pessimistic Locking
The following are the disadvantages of pessimistic locking:
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.
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 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:
AllFieldsLockingPolicy
, the Unit of Work checks all table fields that are part of the SQL where
clause. If any values have changed since the object was read, the update or delete fails. This comparison is only on a per table basis. If you perform an update on an object mapped to multiple tables (including multiple table inheritance), only the changed table(s) appear in the where
clause.
ChangedFieldsLockingPolicy
, the Unit of Work checks only the modified fields. This allows multiple clients to modify different parts of the same row without failure. Using this policy, a delete compares only on the primary key.
SelectedFieldsLockingPolicy
, the Unit of Work compares a list of selected fields in the update statement.
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.
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:
VersionLockingPolicy
, add a numeric field to the database.
TimestampLockingPolicy
, add a timestamp field to the database.
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:
VersionLockingPolicy
increments the value in the version field by one.
TimestampLockingPolicy
inserts a new timestamp into the row. The timestamp is configurable to get the time from the server or the local machine.
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.
When choosing a locking policy, consider the following:
VersionLockingPolicy
. This policy uses integers for field locking and guarantees that you recognize changes.
TimestampLockingPolicy
.
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:
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:
DatabaseRows
.
DatabaseRows
.
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:
Accessor
interface
If you do not define your own accessor, the SDK creates an instance of oracle.toplink.sdk.SDKAccessor
and uses it during execution.
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.
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.
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)
.
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.
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.
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.
Example of an SDK Field Value to | Reference |
---|---|
Read a single nested row |
|
Write a single nested row |
|
Read nested direct values |
|
Write nested direct values |
|
Read nested rows |
|
Write nested rows |
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.
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.
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.
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.
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.
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.
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.
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:
oracle.toplink.sdk.AbstractSDKCall
. This enables you to use the SDKFieldTranslator
class.
SDKFieldTranslators
into your own calls.
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.
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.
/* 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");
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.
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.
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".
The code required to build a basic SDKDescriptor
is almost identical to that used to build a normal 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.
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.
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.
// "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.
If your data store provides support for sequencing, you can configure your descriptor to use sequence numbers.
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.
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".
The SDKDescriptor
supports most other descriptor properties without any special consideration, including:
The OracleAS TopLink SDK does not support the following descriptor properties:
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.
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[]
).
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.
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
.
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.
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.
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" .
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));
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.
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.
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));
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".
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.
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));
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.
Because the many-to-many mapping depends on the relational implementation of many-to-many relationships, the OracleAS TopLink SDK does not support it.
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".
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").
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" .
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" .
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" .
The OracleAS TopLink SDK provides four new mappings that support non-normalized, hierarchical data:
The SDK aggregate object mapping is similar to the standard aggregate object mapping, but differs as follows:
isNullAllowed
flag. Because the fields used to build the aggregate object appear in a single field in the base database row, there is no need to specify how to handle null field values. If the attribute is null, then the field value in the base database row is also null. If the attribute contains an instance of the aggregate object with all null attributes, then the field value in the base database row is an SDK field value with a single, nested database row whose field values are all null.
The code to build an SDK aggregate object mapping is similar to that for the aggregate object mapping. Specify an attribute name, a reference class, and a field name.
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.
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.
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");
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);
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.
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.
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.
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.
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);
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();
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.
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.
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.
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"); ... }
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);
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.
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.
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.
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);
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
}
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:
For more information about acquiring a session, see "Session Manager".
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:
sequenceNameFieldName
(as set in the SDK login); the field value is the name of the sequence number updated by the call.
sequenceCounterFieldName
(as set in the SDK login); the field value is the new value for the sequence number identified by the first field.
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");
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.
Project project = new Project(login);
project.addDescriptor(buildEmployeeDescriptor());
project.addDescriptor(buildAddressDescriptor());
project.addDescriptor(buildProjectDescriptor());
// etc.
After you build your OracleAS TopLink project, obtain a database session (or server session) and log in.
DatabaseSession session = project.createDatabaseSession(); session.login();
When you finish with the session, log out.
session.logout();
The OracleAS TopLink SDK does not offer support for the following regular OracleAS TopLink features:
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:
The default XML extension is similar to a regular OracleAS TopLink project. Use the following steps to develop your application:
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();
Project project = new Project(login);
project.addDescriptor(buildEmployeeDescriptor());
project.addDescriptor(buildAddressDescriptor());
project.addDescriptor(buildProjectDescriptor());
// etc.
XMLDescriptors
.
XMLDescriptor descriptor = new XMLDescriptor();
descriptor.setJavaClass(Employee.class);
descriptor.setRootElementName("employee");
descriptor.setPrimaryKeyElementName("id");
descriptor.setSequenceNumberName("employee");
descriptor.setSequenceNumberElementName("id");
// etc.
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);
DatabaseSession session = project.createDatabaseSession();session.login();
(new XMLSchemaManager(session)).createSequences();
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();
session.logout();
You can customize the OracleAS TopLink XML extension in two key ways, by modifying:
XMLAccessor
interface
XMLTranslator
interface
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.
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.
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:
C:\EmployeeDB
).
C:\EmployeeDB\employee
).
C:\EmployeeDB\employee\1234
).
.xml
(for example: C:\EmployeeDB\employee\1234.xml
).
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.
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:
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)
.
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
.
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 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:
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.
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.
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.
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.
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.
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 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.
XMLDataReadCall call = new XMLDataReadCall(); call.setRootElementName("employee"); call.setPrimaryKeyElementName("id");
The following subclasses provide data call functionality:
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.
XMLDataReadCall call = new XMLDataReadCall(); call.setRootElementName("employee"); call.setPrimaryKeyElementName("id"); call.setResultElementName("salary"); call.setResultElementType(java.math.BigDecimal.class);
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.
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.
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.
An XMLDescriptor
is a subclass of the SDKDescriptor
that:
setRootElementName(String)
method replaces the setTableName(String)
method, setPrimaryKeyElementName(String)
replaces setPrimaryKeyFieldName(String)
, and so on.
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 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:
For more information about file name resolution, see "XML File Accessor" . The default is the current working directory.
login.setBaseDirectoryName("C:\Employee Database");
.xml
.
login.setFileExtension(".xml");
login.setCreatesDirectoriesAsNeeded(true);
login.setSequenceRootElementName("sequence"); login.setSequenceNameElementName("name"); login.setSequenceCounterElementName("count");
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.
DatabaseSession session = project.createDatabaseSession(); session.login(); SchemaManager manager = new XMLSchemaManager(session); manager.createSequences();
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.
XMLLogin login = new XMLLogin();
login.setAccessorClass(XMLJMSAccessor.class);
login.setUserName("user");
login.setPassword("password");
// etc.
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:
read(java.io.Reader)
method takes a Reader
that streams over an XML document, converts that document into a database row, and returns that database row.
write(java.io.Writer, DatabaseRow)
method takes a database row, converts it into an XML document, and writes that document out on the Writer
.
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:
XMLDataStoreException
. The table name is the root element name of the XML document.
<?xml version="1.0"?> <employee> <!-- field values will go here --> </employee>
<?xml version="1.0"?> <employee> <id>1</id> <firstName>Grace</firstName> <lastName>Hopper</lastName> </employee>
<managedEmployees null="true"/>
The DefaultXMLTranslator
delegates the translation to two other classes:
DatabaseRowToXMLTranslator
builds an XML document from a database row and writes it onto a stream.
XMLToDatabaseRowTranslator
reads the XML document from a stream and builds a database row.
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.
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);
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");
Zip file support requires the following two packages, stored in the package oracle.toplink.xml.zip
:
|
Copyright © 2000, 2003 Oracle Corporation. All Rights Reserved. |
|