License     Codehaus     OpenEJB     OpenJMS     OpenORB     Tyrex     

Old releases
  General
  Release 1.3
  Release 1.3rc1
  Release 1.2

Main
  Home
  About
  Features
  Download
  Dependencies
  Reference guide
  Publications
  JavaDoc
  Maven 2 support
  Maven 2 archetypes
  DTD & Schemas
  Recent HTML changes
  News Archive
  RSS news feed
  Project Wiki

Development/Support
  Mailing Lists
  SVN/JIRA
  Contributing
  Support
  Continuous builds
  Prof. services

Related projects
  Spring ORM support
  Spring XML factories
  WS frameworks

XML
  XML

XML Code Generator
  XML Code Generator

JDO
  Introduction
  First steps
  Using JDO
  JDO Config
  Types
  JDO Mapping
  JDO FAQ
  JDO Examples
  JDO HOW-TOs
  Tips & Tricks
  Other Features
  JDO sample JAR

Tools
  Schema generator

Advanced JDO
  Caching
  OQL
  Trans. & Locks
  Design
  KeyGen
  Long Trans.
  Nested Attrs.
  Pooling Examples
  LOBs
  Best practice

DDL Generator
  Using DDL Generator
  Properties
  Ant task
  Type Mapping

More
  The Examples
  3rd Party Tools
  JDO Tests
  XML Tests
  Configuration
 
 

About
  License
  User stories
  Contributors
  Marketplace
  Status, Todo
  Changelog
  Library
  Contact
  Project Name

  



Using Castor JDO


Opening A JDO Database
    Stand-alone application
    J2EE Application
Using A JDO Database to perform persistence operations
    Transient And Persistent Objects
    Running an OQL Query
    Creating a persistent object
    Removing a persistent object
    Updating a persistent object
Using JDO And XML


Opening A JDO Database

Castor JDO supports two type of environments, client applications and J2EE servers. Client applications are responsible for configuring the database connection and managing transactions explicitly. J2EE applications use JNDI to obtain a pre-configured database connection, and use UserTransaction or container managed transactions (CMT) to manage transactions. If you have been using JDBC in these two environments, you will be readily familiar with the two models and the differences between them.

Stand-alone application

Client applications are responsible for defining the JDO configuration, and managing the transaction explicitly. The database is by default configured through a separate XML file which links to the mapping file. Alternatively, it can be configured using the utility class JDOConfFactory.

In the example code I refer to the JDO configuration file as jdo-conf.xml, but any name can be used. See Castor JDO Configuration for more information.

As of release 0.9.6, a new JDOManager class is provided, that replaces the former JDO class. Any new features will be implemented against the new JDOManager class only.

As with its predecessor, org.exolab.castor.jdo.JDOManager defines the database name and properties and is used to open a database connection. An instance of this class is constructed with a two-step approach:

  1. Statically load the JDO configuration file through one of the loadConfiguration() methods, e.g. loadConfiguration(java.lang.String).
  2. Create an instance of the JDO engine using the factory method createInstance(java.lang.String) where you supply one of the database names defined in the configuration file loaded in step 1).

The org.exolab.castor.jdo.Database object represents an open connection to the database. By definition the database object is not thread safe and should not be used from concurrent threads. There is little overhead involved in opening multiple Database objects, and a JDBC connection is acquired only per open transaction.

The following code fragment creates an instance of JDOManager for a database 'mybd', opens a database, performs a transaction, and closes the database - as it will typically appear in client applications (for brevity, we have ommitted any required exception handling):

 
JDOManager jdoManager;
Database db;
 
// load the JDO configuration file and construct a new JDOManager for the database 'mydb'
JDOManager.loadConfiguration("jdo-conf.xml");
jdoManager = JDOManager.createInstance("mydb");

// Obtain a new database
Database db = jdoManager.getDatabase();
    
// Begin a transaction
db.begin();

// Do something
. . .

// Commit the transaction and close the database
db.commit();
db.close();
         

For an example showing how to set up a database configuration on the fly without the need of a preconfigured XML configuration file) see JdoConfFactory.

J2EE Application

J2EE applications depend on the J2EE container (Servlet, EJB, etc) to configure the database connection and use JNDI to look it up. This model allows the application deployer to configure the database properties from a central place, and gives the J2EE container the ability to manage distributed transactions across multiple data sources.

Instead of constructing a org.exolab.castor.jdo.JDOManager the application uses the JNDI namespace to look it up. We recommend enlisting the JDOManager object under the java:comp/env/jdo namespace, compatible with the convention for listing JDBC resources.

The following code fragment uses JNDI to lookup a database, and uses the JTA UserTransaction interface to manage the transaction:

InitialContext  ctx;
UserTransaction ut;
Database        db;

// Lookup databse in JNDI
ctx = new InitialContext();
db = (Database) ctx.lookup( "java:comp/env/jdo/mydb" );

// Begin a transaction
ut = (UserTransaction) ctx.lookup( "java:comp/UserTransaction" );
ut.begin();
// Do something
. . .
// Commit the transaction, close database
ut.commit();
db.close();
           

If the transaction is managed by the container, a common case with EJB beans and in particular entity beans, there is no need to begin/commit the transaction explicitly. Instead the application server takes care of enlisting the database in the ongoing transaction and executes commit/rollback at the proper time.

The following code snippet relies on the container to manage the transaction

InitialContext  ctx;
UserTransaction ut;
Database        db;

// Lookup databse in JNDI
ctx = new InitialContext();
db = (Database) ctx.lookup( "java:comp/env/jdo/mydb" );

// Do something
. . .
// Close the database
db.close();
        

Using A JDO Database to perform persistence operations

Transient And Persistent Objects

All JDO operations occur within the context of a transaction. JDO works by loading data from the database into an object in memory, allowing the application to modify the object, and then storing the object's new state when the transaction commits. All objects can be in one of two states: transient or persistent.

Transient: Any object whose state will not be saved to the database when the transaction commits. Changes to transient objects will not be reflected in the database.

Persistent: Any object whose state will be saved to the database when the transaction commits. Changes to persistent objects will be reflected in the database.

An object becomes persistent in one of two ways: it is the result of a query, (and the query is not performed in read-only mode) or it is added to the database using create(java.lang.Object) or update(java.lang.Object). All objects that are not persistent are transient. When the transaction commits or rolls back, all persistent objects become transient.

In a client application, use begin(), commit() and rollback() to manage transactions. In a J2EE application, JDO relies on the container to manage transactions either implicitly (based on the transaction attribute of a bean) or explicitly using the javax.transaction.UserTransaction interface.

If a persistent object was modified during the transaction, at commit time the modifications are stored back to the database. If the transaction rolls back, no modifications will be made to the database. Once the transaction completes, the object is once again transient. To use the same object in two different transactions, you must query it again.

An object is transient or persistent from the view point of the database to which the transaction belongs. An object is generally persistent in a single database, and calling isPersistent(java.lang.Object) from another database will return false. It is possible to make an object persistent in two database, e.g. by querying it in one, and creating it in the other.

Running an OQL Query

OQL queries are used to lookup and query objects from the database. OQL queries are similar to SQL queries, but use object names instead of SQL names and do not require join clauses. For example, if the object being loaded is of type TestObject, the OQL query will load FROM TestObject, whether the actual table name in the database is test, test_object, or any other name. If a join is required to load related objects, Castor will automatically perform the join.

The following code snippet uses an OQL query to load all the objects in a given group. Note that product and group are related objects, the JDBC query involves a join:

OQLQuery     oql;
QueryResults results;

// Explicitly begin transaction
db.begin();

// Construct a new query and bind its parameters
oql = db.getOQLQuery("SELECT p FROM Product p WHERE Group=$1");
oql.bind(groupId);

// Retrieve results and print each one
results = oql.execute();
while (results.hasMore()) {
  System.out.println(results.next());
}

// Explicitly close the QueryResults
results.close();

// Explicitly close the OQLQuery
oql.close();

// Explicitly commit transaction
db.commit();
db.close();
           

The following code snippet uses the previous query to obtain products, mark down their price by 25%, and store them back to the database (in this case using a client application transaction):

OQLQuery     oql;
QueryResults results;

// Explicitly begin transaction
db.begin();

// Construct a new query and bind its parameters
oql = db.getOQLQuery("SELECT p FROM Product p WHERE Group=$1");
oql.bind(groupId);

// Retrieve results and mark up each one by 25%
Product prod;
while (results.hasMore()) {
  prod = (Product) results.next();
  prod.markDown(0.25);
  prod.setOnSale(true);
}

// Explicitly close the QueryResults
results.close();

// Explicitly close the OQLQuery
oql.close();

// Explicitly commit transaction
db.commit();
db.close();
        

As illustrated above, a query is executed in three steps. First a query object is created from the database using an OQL statement. If there are any parameters, the second step involves binding these parameters. Numbered parameters are bound using the order specified in their names. (e.g. first $1, after that $2, and so on...) The third step involves executing the query and obtaining a result set of type org.exolab.castor.jdo.QueryResults.

A query can be created once and executed multiple times. Each time it is executed the bound parameters are lost, and must be supplied a second time. The result of a query can be used while the query is being executed a second time.

There is also a special form of query that gives a possibility to call stored procedures:

oql = db.getOQLQuery("CALL sp_something($) AS myapp.Product");
           

Here sp_something is a stored procedure returning one or more ResultSets with the same sequence of fields as Castor-generated SELECT for the OQL query "SELECT p FROM myapp.Product p" (for objects without relations the sequence is: identity, then all other fields in the same order as in mapping.xml).

Creating a persistent object

The method create(java.lang.Object) creates a new object in the database, or in JDO terminology makes a transient object persistent. An object created with the create method will remain in the database if the transaction commits; if the transaction rolls back the object will be removed from the database. An exception is thrown if an object with the same identity already exists in the database.

The following code snippet creates a new product with a group that was previously queried:

Database db = ...;
db.begin();        

//load product group
ProductGroup furnitures = db.load(...);

// Create the Product object
Product prod;
prod = new Product();
prod.setSku(5678);
prod.setName("Plastic Chair");
prod.setPrice(55.0 );
prod.setGroup(furnitures);

// Make it persistent
db.create(prod);

db.commit();
        

Removing a persistent object

The method remove(java.lang.Object) has the reverse effect, deleting a persistent object. Once removed the object is no longer visible to any transaction. If the transaction commits, the object will be removed from the database, however, if the transaction rolls back the object will remain in the database. An exception is thrown when attempting to remove an object that is not persistent.

The following code snippet deletes the previously created Product instance:

Database db = ...;
db.begin();        

// load the Product instance with sku = 5678
Product prod = db.load (Product.class, new Integer(5678);

// delete the Product instance
db.remove(prod);

db.commit();
        

Updating a persistent object

There's no special method offering on the org.exolab.castor.jdo.Database to update an existing persistent object. Simply load the object to be updated, change one or more of its properties, and commit the transaction. Castor JDO will automatically figure that that the object in question has changed and will persist these changes to the underlying database.

The following code snippet loads a previously created Product instance, changes its description property and commits the transaction.

Database db = ...;
db.begin();        

// load the Product instance with sku = 5678
Product prod = db.load (Product.class, new Integer(5678);

// change the object properties
prod.setDescription("New plastic chair");

//commit the transaction
db.commit();
        

Using JDO And XML

Castor JDO and Castor XML can be combined to perform transactional database operations that use XML as the form of input and output. The following code snippet uses a combination of persistent and transient objects to describe a financial operation.

This example retrieves two account objects and moves an amount from one account to the other. The transfer is described using a transient object (i.e. no record in the database), which is then used to generate an XML document describing the transfer. An extra step (not shown here), uses XSLT to transform the XML document into an HTML page.

Transfer tran;
Account  from;
Account  to;
OQLQuery oql;

tran = new Transfer();

// Construct a query and load the two accounts
oql = db.getOQLQuery("SELECT a FROM Account a WHERE Id=$");
oql.bind(fromId);
from = oql.execute().nextElement();
oql.bind(toId);
to = oql.execute().nextElement();

// Move money from one account to the other
if (from.getBalance() >= amount) {
  from.decBalance(amount);
  to.incBalance(amount);
  trans.setStatus(Transfer.COMPLETE);
  trans.setAccount(from);
  trans.setAmount(amount);
} else {
  // Report an overdraft
  trans.setStatus( Transfer.OVERDRAFT );
}

// Produce an XML describing the transfer
Marshaller.marshal(trans, outputStream);
      

The XML produced by the above code might look like:

<?xml version="1.0"?>
<report>
  <status>Completed</status>
  <account id="1234-5678-90" balance="50"/>
  <transfer amount="49.99"/>
</report>

 
   
  
   
 


Copyright © 1999-2005 ExoLab Group, Intalio Inc., and Contributors. All rights reserved.
 
Java, EJB, JDBC, JNDI, JTA, Sun, Sun Microsystems are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and in other countries. XML, XML Schema, XSLT and related standards are trademarks or registered trademarks of MIT, INRIA, Keio or others, and a product of the World Wide Web Consortium. All other product names mentioned herein are trademarks of their respective owners.